blob: cb811419496807cfcdce51b9ffe3289e3a624dda [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
Michal Vasko3031aae2016-01-27 16:07:18 +010029struct nc_server_ssh_opts ssh_ch_opts = {
30 .auth_methods = NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE,
31 .auth_attempts = 3,
32 .auth_timeout = 10
33};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010034pthread_mutex_t ssh_ch_opts_lock = PTHREAD_MUTEX_INITIALIZER;
Michal Vasko086311b2016-01-08 09:53:11 +010035extern struct nc_server_opts server_opts;
Michal Vaskob05053d2016-01-22 16:12:06 +010036
Michal Vasko3031aae2016-01-27 16:07:18 +010037API int
Michal Vaskoda514772016-02-01 11:32:01 +010038nc_server_ssh_endpt_set_address(const char *endpt_name, const char *address)
39{
40 return nc_server_endpt_set_address_port(endpt_name, address, 0, NC_TI_LIBSSH);
41}
42
43API int
44nc_server_ssh_endpt_set_port(const char *endpt_name, uint16_t port)
45{
46 return nc_server_endpt_set_address_port(endpt_name, NULL, port, NC_TI_LIBSSH);
47}
48
Michal Vaskob05053d2016-01-22 16:12:06 +010049static int
Michal Vaskoe2713da2016-08-22 16:06:40 +020050nc_server_ssh_add_hostkey(const char *privkey_path, struct nc_server_ssh_opts *opts)
Michal Vasko086311b2016-01-08 09:53:11 +010051{
Michal Vasko1a38c862016-01-15 15:50:07 +010052 if (!privkey_path) {
Michal Vasko45e53ae2016-04-07 11:46:03 +020053 ERRARG("privkey_path");
Michal Vasko086311b2016-01-08 09:53:11 +010054 return -1;
55 }
56
Michal Vaskoe2713da2016-08-22 16:06:40 +020057 if (eaccess(privkey_path, R_OK)) {
58 ERR("Host key \"%s\" cannot be read (%s).", privkey_path, strerror(errno));
Michal Vasko5fcc7142016-02-02 12:21:10 +010059 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +010060 }
Michal Vaskod45e25a2016-01-08 15:48:44 +010061
Michal Vaskoe2713da2016-08-22 16:06:40 +020062 ++opts->hostkey_count;
63 opts->hostkeys = nc_realloc(opts->hostkeys, opts->hostkey_count * sizeof *opts->hostkeys);
64 if (!opts->hostkeys) {
65 ERRMEM;
66 return -1;
67 }
68 opts->hostkeys[opts->hostkey_count - 1] = lydict_insert(server_opts.ctx, privkey_path, 0);
69
Michal Vasko5fcc7142016-02-02 12:21:10 +010070 return 0;
Michal Vaskob05053d2016-01-22 16:12:06 +010071}
72
73API int
Michal Vaskoe2713da2016-08-22 16:06:40 +020074nc_server_ssh_endpt_add_hostkey(const char *endpt_name, const char *privkey_path)
Michal Vaskob05053d2016-01-22 16:12:06 +010075{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010076 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +010077 struct nc_endpt *endpt;
78
Michal Vasko51e514d2016-02-02 15:51:52 +010079 /* LOCK */
Michal Vaskoe2713da2016-08-22 16:06:40 +020080 endpt = nc_server_endpt_lock(endpt_name, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +010081 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +010082 return -1;
83 }
Michal Vaskoe2713da2016-08-22 16:06:40 +020084 ret = nc_server_ssh_add_hostkey(privkey_path, endpt->ssh_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +010085 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010086 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +010087
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010088 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +010089}
90
91API int
Michal Vaskoe2713da2016-08-22 16:06:40 +020092nc_server_ssh_ch_add_hostkey(const char *privkey_path)
Michal Vaskob05053d2016-01-22 16:12:06 +010093{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010094 int ret;
95
96 /* OPTS LOCK */
97 pthread_mutex_lock(&ssh_ch_opts_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +020098 ret = nc_server_ssh_add_hostkey(privkey_path, &ssh_ch_opts);
99 /* OPTS UNLOCK */
100 pthread_mutex_unlock(&ssh_ch_opts_lock);
101
102 return ret;
103}
104
105static int
106nc_server_ssh_del_hostkey(const char *privkey_path, struct nc_server_ssh_opts *opts)
107{
108 uint8_t i;
109
110 if (!privkey_path) {
111 for (i = 0; i < opts->hostkey_count; ++i) {
112 lydict_remove(server_opts.ctx, opts->hostkeys[i]);
113 }
114 free(opts->hostkeys);
115 opts->hostkeys = NULL;
116 opts->hostkey_count = 0;
117 } else {
118 for (i = 0; i < opts->hostkey_count; ++i) {
119 if (!strcmp(opts->hostkeys[i], privkey_path)) {
120 --opts->hostkey_count;
121 lydict_remove(server_opts.ctx, opts->hostkeys[i]);
122 if (i < opts->hostkey_count - 1) {
123 memmove(opts->hostkeys + i, opts->hostkeys + i + 1, (opts->hostkey_count - i) * sizeof *opts->hostkeys);
124 }
125 return 0;
126 }
127 }
128
129 ERR("Host key \"%s\" not found.", privkey_path);
130 return -1;
131 }
132
133 return 0;
134}
135
136API int
137nc_server_ssh_endpt_del_hostkey(const char *endpt_name, const char *privkey_path)
138{
139 int ret;
140 struct nc_endpt *endpt;
141
142 /* LOCK */
143 endpt = nc_server_endpt_lock(endpt_name, NULL);
144 if (!endpt) {
145 return -1;
146 }
147 ret = nc_server_ssh_del_hostkey(privkey_path, endpt->ssh_opts);
148 /* UNLOCK */
149 nc_server_endpt_unlock(endpt);
150
151 return ret;
152}
153
154API int
155nc_server_ssh_ch_del_hostkey(const char *privkey_path)
156{
157 int ret;
158
159 /* OPTS LOCK */
160 pthread_mutex_lock(&ssh_ch_opts_lock);
161 ret = nc_server_ssh_del_hostkey(privkey_path, &ssh_ch_opts);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100162 /* OPTS UNLOCK */
163 pthread_mutex_unlock(&ssh_ch_opts_lock);
164
165 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100166}
167
168static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100169nc_server_ssh_set_banner(const char *banner, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100170{
Michal Vaskob05053d2016-01-22 16:12:06 +0100171 if (!banner) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200172 ERRARG("banner");
Michal Vaskob05053d2016-01-22 16:12:06 +0100173 return -1;
174 }
175
Michal Vaskoe2713da2016-08-22 16:06:40 +0200176 if (opts->banner) {
177 lydict_remove(server_opts.ctx, opts->banner);
Michal Vaskob05053d2016-01-22 16:12:06 +0100178 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200179 opts->banner = lydict_insert(server_opts.ctx, banner, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +0100180 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100181}
182
183API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100184nc_server_ssh_endpt_set_banner(const char *endpt_name, const char *banner)
Michal Vasko086311b2016-01-08 09:53:11 +0100185{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100186 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100187 struct nc_endpt *endpt;
188
Michal Vasko51e514d2016-02-02 15:51:52 +0100189 /* LOCK */
Michal Vaskoe2713da2016-08-22 16:06:40 +0200190 endpt = nc_server_endpt_lock(endpt_name, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100191 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100192 return -1;
193 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200194 ret = nc_server_ssh_set_banner(banner, endpt->ssh_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100195 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100196 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100197
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100198 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100199}
200
201API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100202nc_server_ssh_ch_set_banner(const char *banner)
Michal Vasko086311b2016-01-08 09:53:11 +0100203{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100204 int ret;
205
206 /* OPTS LOCK */
207 pthread_mutex_lock(&ssh_ch_opts_lock);
208 ret = nc_server_ssh_set_banner(banner, &ssh_ch_opts);
209 /* OPTS UNLOCK */
210 pthread_mutex_unlock(&ssh_ch_opts_lock);
211
212 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100213}
214
215static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100216nc_server_ssh_set_auth_methods(int auth_methods, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100217{
Michal Vasko086311b2016-01-08 09:53:11 +0100218 if (!(auth_methods & NC_SSH_AUTH_PUBLICKEY) && !(auth_methods & NC_SSH_AUTH_PASSWORD)
219 && !(auth_methods & NC_SSH_AUTH_INTERACTIVE)) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200220 ERRARG("auth_methods");
Michal Vasko086311b2016-01-08 09:53:11 +0100221 return -1;
222 }
223
Michal Vaskob05053d2016-01-22 16:12:06 +0100224 opts->auth_methods = auth_methods;
225 return 0;
226}
227
228API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100229nc_server_ssh_endpt_set_auth_methods(const char *endpt_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100230{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100231 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100232 struct nc_endpt *endpt;
233
Michal Vasko51e514d2016-02-02 15:51:52 +0100234 /* LOCK */
Michal Vaskoe2713da2016-08-22 16:06:40 +0200235 endpt = nc_server_endpt_lock(endpt_name, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100236 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100237 return -1;
238 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200239 ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->ssh_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100240 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100241 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100242
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100243 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100244}
245
246API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100247nc_server_ssh_ch_set_auth_methods(int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100248{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100249 int ret;
250
251 /* OPTS LOCK */
252 pthread_mutex_lock(&ssh_ch_opts_lock);
253 ret = nc_server_ssh_set_auth_methods(auth_methods, &ssh_ch_opts);
254 /* OPTS UNLOCK */
255 pthread_mutex_unlock(&ssh_ch_opts_lock);
256
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_attempts(uint16_t auth_attempts, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100262{
Michal Vaskob05053d2016-01-22 16:12:06 +0100263 if (!auth_attempts) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200264 ERRARG("auth_attempts");
Michal Vaskob05053d2016-01-22 16:12:06 +0100265 return -1;
266 }
267
Michal Vaskob05053d2016-01-22 16:12:06 +0100268 opts->auth_attempts = auth_attempts;
Michal Vasko086311b2016-01-08 09:53:11 +0100269 return 0;
270}
271
272API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100273nc_server_ssh_endpt_set_auth_attempts(const char *endpt_name, uint16_t auth_attempts)
Michal Vasko086311b2016-01-08 09:53:11 +0100274{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100275 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100276 struct nc_endpt *endpt;
277
Michal Vasko51e514d2016-02-02 15:51:52 +0100278 /* LOCK */
Michal Vaskoe2713da2016-08-22 16:06:40 +0200279 endpt = nc_server_endpt_lock(endpt_name, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100280 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100281 return -1;
282 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200283 ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->ssh_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100284 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100285 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100286
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100287 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100288}
289
290API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100291nc_server_ssh_set_ch_auth_attempts(uint16_t auth_attempts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100292{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100293 int ret;
294
295 /* OPTS LOCK */
296 pthread_mutex_lock(&ssh_ch_opts_lock);
297 ret = nc_server_ssh_set_auth_attempts(auth_attempts, &ssh_ch_opts);
298 /* OPTS UNLOCK */
299 pthread_mutex_unlock(&ssh_ch_opts_lock);
300
301 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100302}
303
304static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100305nc_server_ssh_set_auth_timeout(uint16_t auth_timeout, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100306{
Michal Vaskob05053d2016-01-22 16:12:06 +0100307 if (!auth_timeout) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200308 ERRARG("auth_timeout");
Michal Vasko086311b2016-01-08 09:53:11 +0100309 return -1;
310 }
311
Michal Vaskob05053d2016-01-22 16:12:06 +0100312 opts->auth_timeout = auth_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100313 return 0;
314}
315
316API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100317nc_server_ssh_endpt_set_auth_timeout(const char *endpt_name, uint16_t auth_timeout)
Michal Vasko086311b2016-01-08 09:53:11 +0100318{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100319 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100320 struct nc_endpt *endpt;
321
Michal Vasko51e514d2016-02-02 15:51:52 +0100322 /* LOCK */
Michal Vaskoe2713da2016-08-22 16:06:40 +0200323 endpt = nc_server_endpt_lock(endpt_name, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100324 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100325 return -1;
326 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200327 ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->ssh_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100328 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100329 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100330
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100331 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100332}
333
334API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100335nc_server_ssh_ch_set_auth_timeout(uint16_t auth_timeout)
Michal Vaskob05053d2016-01-22 16:12:06 +0100336{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100337 int ret;
338
339 /* OPTS LOCK */
340 pthread_mutex_lock(&ssh_ch_opts_lock);
341 ret = nc_server_ssh_set_auth_timeout(auth_timeout, &ssh_ch_opts);
342 /* OPTS UNLOCK */
343 pthread_mutex_unlock(&ssh_ch_opts_lock);
344
345 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100346}
347
348static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100349nc_server_ssh_add_authkey(const char *pubkey_path, const char *username, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100350{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200351 if (!pubkey_path) {
352 ERRARG("pubkey_path");
353 return -1;
354 } else if (!username) {
355 ERRARG("username");
Michal Vasko086311b2016-01-08 09:53:11 +0100356 return -1;
357 }
358
Michal Vaskob05053d2016-01-22 16:12:06 +0100359 ++opts->authkey_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100360 opts->authkeys = nc_realloc(opts->authkeys, opts->authkey_count * sizeof *opts->authkeys);
361 if (!opts->authkeys) {
362 ERRMEM;
363 return -1;
364 }
Michal Vaskob05053d2016-01-22 16:12:06 +0100365 opts->authkeys[opts->authkey_count - 1].path = lydict_insert(server_opts.ctx, pubkey_path, 0);
366 opts->authkeys[opts->authkey_count - 1].username = lydict_insert(server_opts.ctx, username, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +0100367
Michal Vasko086311b2016-01-08 09:53:11 +0100368 return 0;
369}
370
371API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100372nc_server_ssh_endpt_add_authkey(const char *endpt_name, const char *pubkey_path, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100373{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100374 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100375 struct nc_endpt *endpt;
376
Michal Vasko51e514d2016-02-02 15:51:52 +0100377 /* LOCK */
Michal Vaskoe2713da2016-08-22 16:06:40 +0200378 endpt = nc_server_endpt_lock(endpt_name, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100379 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100380 return -1;
381 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200382 ret = nc_server_ssh_add_authkey(pubkey_path, username, endpt->ssh_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100383 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100384 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100385
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100386 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100387}
388
389API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100390nc_server_ssh_ch_add_authkey(const char *pubkey_path, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100391{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100392 int ret;
393
394 /* OPTS LOCK */
395 pthread_mutex_lock(&ssh_ch_opts_lock);
396 ret = nc_server_ssh_add_authkey(pubkey_path, username, &ssh_ch_opts);
397 /* OPTS UNLOCK */
398 pthread_mutex_unlock(&ssh_ch_opts_lock);
399
400 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100401}
402
403static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100404nc_server_ssh_del_authkey(const char *pubkey_path, const char *username, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100405{
Michal Vasko086311b2016-01-08 09:53:11 +0100406 uint32_t i;
407 int ret = -1;
408
Michal Vasko1a38c862016-01-15 15:50:07 +0100409 if (!pubkey_path && !username) {
Michal Vaskob05053d2016-01-22 16:12:06 +0100410 for (i = 0; i < opts->authkey_count; ++i) {
411 lydict_remove(server_opts.ctx, opts->authkeys[i].path);
412 lydict_remove(server_opts.ctx, opts->authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100413
Michal Vasko086311b2016-01-08 09:53:11 +0100414 ret = 0;
415 }
Michal Vaskob05053d2016-01-22 16:12:06 +0100416 free(opts->authkeys);
417 opts->authkeys = NULL;
418 opts->authkey_count = 0;
Michal Vasko1a38c862016-01-15 15:50:07 +0100419 } else {
Michal Vaskob05053d2016-01-22 16:12:06 +0100420 for (i = 0; i < opts->authkey_count; ++i) {
421 if ((!pubkey_path || !strcmp(opts->authkeys[i].path, pubkey_path))
422 && (!username || !strcmp(opts->authkeys[i].username, username))) {
Michal Vaskob05053d2016-01-22 16:12:06 +0100423 lydict_remove(server_opts.ctx, opts->authkeys[i].path);
424 lydict_remove(server_opts.ctx, opts->authkeys[i].username);
Michal Vasko1a38c862016-01-15 15:50:07 +0100425
Michal Vaskob05053d2016-01-22 16:12:06 +0100426 --opts->authkey_count;
Michal Vaskoc0256492016-02-02 12:19:06 +0100427 if (i < opts->authkey_count) {
428 memcpy(&opts->authkeys[i], &opts->authkeys[opts->authkey_count], sizeof *opts->authkeys);
429 } else if (!opts->authkey_count) {
430 free(opts->authkeys);
431 opts->authkeys = NULL;
432 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100433
434 ret = 0;
435 }
436 }
Michal Vasko086311b2016-01-08 09:53:11 +0100437 }
438
439 return ret;
440}
441
Michal Vaskob05053d2016-01-22 16:12:06 +0100442API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100443nc_server_ssh_endpt_del_authkey(const char *endpt_name, const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100444{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100445 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100446 struct nc_endpt *endpt;
447
Michal Vasko51e514d2016-02-02 15:51:52 +0100448 /* LOCK */
Michal Vaskoe2713da2016-08-22 16:06:40 +0200449 endpt = nc_server_endpt_lock(endpt_name, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100450 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100451 return -1;
452 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200453 ret = nc_server_ssh_del_authkey(pubkey_path, username, endpt->ssh_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100454 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100455 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100456
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100457 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100458}
459
460API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100461nc_server_ssh_ch_del_authkey(const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100462{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100463 int ret;
464
465 /* OPTS LOCK */
466 pthread_mutex_lock(&ssh_ch_opts_lock);
467 ret = nc_server_ssh_del_authkey(pubkey_path, username, &ssh_ch_opts);
468 /* OPTS UNLOCK */
469 pthread_mutex_unlock(&ssh_ch_opts_lock);
470
471 return ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100472}
473
474void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100475nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100476{
Michal Vaskoe2713da2016-08-22 16:06:40 +0200477 nc_server_ssh_del_hostkey(NULL, opts);
478 if (opts->banner) {
479 lydict_remove(server_opts.ctx, opts->banner);
480 opts->banner = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +0100481 }
Michal Vasko3031aae2016-01-27 16:07:18 +0100482 nc_server_ssh_del_authkey(NULL, NULL, opts);
Michal Vaskob05053d2016-01-22 16:12:06 +0100483}
484
Michal Vasko086311b2016-01-08 09:53:11 +0100485API void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100486nc_server_ssh_ch_clear_opts(void)
Michal Vasko086311b2016-01-08 09:53:11 +0100487{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100488 /* OPTS LOCK */
489 pthread_mutex_lock(&ssh_ch_opts_lock);
490 nc_server_ssh_clear_opts(&ssh_ch_opts);
491 /* OPTS UNLOCK */
492 pthread_mutex_unlock(&ssh_ch_opts_lock);
Michal Vasko086311b2016-01-08 09:53:11 +0100493}
494
495static char *
496auth_password_get_pwd_hash(const char *username)
497{
498 struct passwd *pwd, pwd_buf;
499 struct spwd *spwd, spwd_buf;
500 char *pass_hash = NULL, buf[256];
501
502 getpwnam_r(username, &pwd_buf, buf, 256, &pwd);
503 if (!pwd) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100504 VRB("User \"%s\" not found locally.", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100505 return NULL;
506 }
507
508 if (!strcmp(pwd->pw_passwd, "x")) {
509 getspnam_r(username, &spwd_buf, buf, 256, &spwd);
510 if (!spwd) {
511 VRB("Failed to retrieve the shadow entry for \"%s\".", username);
512 return NULL;
513 }
514
515 pass_hash = spwd->sp_pwdp;
516 } else {
517 pass_hash = pwd->pw_passwd;
518 }
519
520 if (!pass_hash) {
Michal Vaskod083db62016-01-19 10:31:29 +0100521 ERR("No password could be retrieved for \"%s\".", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100522 return NULL;
523 }
524
525 /* check the hash structure for special meaning */
526 if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) {
527 VRB("User \"%s\" is not allowed to authenticate using a password.", username);
528 return NULL;
529 }
530 if (!strcmp(pass_hash, "*NP*")) {
531 VRB("Retrieving password for \"%s\" from a NIS+ server not supported.", username);
532 return NULL;
533 }
534
535 return strdup(pass_hash);
536}
537
538static int
539auth_password_compare_pwd(const char *pass_hash, const char *pass_clear)
540{
541 char *new_pass_hash;
542 struct crypt_data cdata;
543
544 if (!pass_hash[0]) {
545 if (!pass_clear[0]) {
546 WRN("User authentication successful with an empty password!");
547 return 0;
548 } else {
549 /* the user did now know he does not need any password,
550 * (which should not be used) so deny authentication */
551 return 1;
552 }
553 }
554
555 cdata.initialized = 0;
556 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
557 return strcmp(new_pass_hash, pass_hash);
558}
559
560static void
561nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
562{
563 char *pass_hash;
564
565 pass_hash = auth_password_get_pwd_hash(session->username);
566 if (pass_hash && !auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100567 VRB("User \"%s\" authenticated.", session->username);
Michal Vasko086311b2016-01-08 09:53:11 +0100568 ssh_message_auth_reply_success(msg, 0);
569 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
570 free(pass_hash);
571 return;
572 }
573
574 free(pass_hash);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100575 ++session->ssh_auth_attempts;
Michal Vasko296fee82016-05-04 08:57:31 +0200576 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100577 ssh_message_reply_default(msg);
578}
579
580static void
581nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
582{
583 char *pass_hash;
584
585 if (!ssh_message_auth_kbdint_is_response(msg)) {
586 const char *prompts[] = {"Password: "};
587 char echo[] = {0};
588
589 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
590 } else {
591 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {
592 ssh_message_reply_default(msg);
593 return;
594 }
595 pass_hash = auth_password_get_pwd_hash(session->username);
596 if (!pass_hash) {
597 ssh_message_reply_default(msg);
598 return;
599 }
600 if (!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0))) {
601 VRB("User \"%s\" authenticated.", session->username);
602 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
603 ssh_message_auth_reply_success(msg, 0);
604 } else {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100605 ++session->ssh_auth_attempts;
606 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100607 ssh_message_reply_default(msg);
608 }
Radek Krejcifb533742016-03-04 15:12:54 +0100609 free(pass_hash);
Michal Vasko086311b2016-01-08 09:53:11 +0100610 }
611}
612
613static const char *
Michal Vasko3031aae2016-01-27 16:07:18 +0100614auth_pubkey_compare_key(struct nc_server_ssh_opts *opts, ssh_key key)
Michal Vasko086311b2016-01-08 09:53:11 +0100615{
616 uint32_t i;
617 ssh_key pub_key;
Michal Vasko76e3a352016-01-18 09:07:00 +0100618 const char *username = NULL;
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200619 int ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100620
Michal Vasko3031aae2016-01-27 16:07:18 +0100621 for (i = 0; i < opts->authkey_count; ++i) {
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200622 ret = ssh_pki_import_pubkey_file(opts->authkeys[i].path, &pub_key);
623 if (ret == SSH_EOF) {
624 WRN("Failed to import the public key \"%s\" (File access problem).", opts->authkeys[i].path);
625 continue;
626 } else if (ret == SSH_ERROR) {
627 WRN("Failed to import the public key \"%s\" (SSH error).", opts->authkeys[i].path);
Michal Vasko086311b2016-01-08 09:53:11 +0100628 continue;
629 }
630
631 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
632 ssh_key_free(pub_key);
633 break;
634 }
635
636 ssh_key_free(pub_key);
637 }
638
Michal Vasko3031aae2016-01-27 16:07:18 +0100639 if (i < opts->authkey_count) {
640 username = opts->authkeys[i].username;
Michal Vasko086311b2016-01-08 09:53:11 +0100641 }
642
643 return username;
644}
645
646static void
647nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
648{
649 const char *username;
650 int signature_state;
651
652 signature_state = ssh_message_auth_publickey_state(msg);
653 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
654 VRB("User \"%s\" authenticated.", session->username);
655 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
656 ssh_message_auth_reply_success(msg, 0);
657 return;
658
659 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Michal Vasko2cc4c682016-03-01 09:16:48 +0100660 if ((username = auth_pubkey_compare_key(session->data, ssh_message_auth_pubkey(msg))) == NULL) {
Michal Vasko086311b2016-01-08 09:53:11 +0100661 VRB("User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
662
663 } else if (strcmp(session->username, username)) {
664 VRB("User \"%s\" is not the username identified with the presented public key.", session->username);
665
666 } else {
667 /* accepting only the use of a public key */
668 ssh_message_auth_reply_pk_ok_simple(msg);
669 return;
670 }
671 }
672
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100673 ++session->ssh_auth_attempts;
674 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100675 ssh_message_reply_default(msg);
676}
677
678static int
Michal Vasko96164bf2016-01-21 15:41:58 +0100679nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100680{
Michal Vasko96164bf2016-01-21 15:41:58 +0100681 ssh_channel chan;
682
683 /* first channel request */
684 if (!session->ti.libssh.channel) {
685 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100686 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100687 return -1;
688 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100689 chan = ssh_message_channel_request_open_reply_accept(msg);
690 if (!chan) {
691 ERR("Failed to create a new SSH channel.");
692 return -1;
693 }
694 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +0100695
Michal Vasko96164bf2016-01-21 15:41:58 +0100696 /* additional channel request */
697 } else {
698 chan = ssh_message_channel_request_open_reply_accept(msg);
699 if (!chan) {
700 ERR("Session %u: failed to create a new SSH channel.", session->id);
701 return -1;
702 }
703 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +0100704 }
705
Michal Vasko086311b2016-01-08 09:53:11 +0100706 return 0;
707}
708
709static int
710nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
711{
Michal Vasko96164bf2016-01-21 15:41:58 +0100712 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +0100713
Michal Vasko96164bf2016-01-21 15:41:58 +0100714 if (strcmp(subsystem, "netconf")) {
715 WRN("Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +0100716 return -1;
717 }
718
Michal Vasko96164bf2016-01-21 15:41:58 +0100719 if (session->ti.libssh.channel == channel) {
720 /* first channel requested */
721 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
722 ERRINT;
723 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100724 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100725 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
726 ERR("Session %u: subsystem \"netconf\" requested for the second time.", session->id);
727 return -1;
728 }
729
730 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +0100731 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +0100732 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
733 new_session = calloc(1, sizeof *new_session);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100734 if (!new_session) {
735 ERRMEM;
736 return -1;
737 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100738
739 /* insert the new session */
740 if (!session->ti.libssh.next) {
741 new_session->ti.libssh.next = session;
742 } else {
743 new_session->ti.libssh.next = session->ti.libssh.next;
744 }
745 session->ti.libssh.next = new_session;
746
747 new_session->status = NC_STATUS_STARTING;
748 new_session->side = NC_SERVER;
749 new_session->ti_type = NC_TI_LIBSSH;
750 new_session->ti_lock = session->ti_lock;
751 new_session->ti.libssh.channel = channel;
752 new_session->ti.libssh.session = session->ti.libssh.session;
753 new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
754 new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
755 new_session->port = session->port;
756 new_session->ctx = server_opts.ctx;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100757 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX
758 | (session->flags & NC_SESSION_CALLHOME ? NC_SESSION_CALLHOME : 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100759 }
760
761 return 0;
762}
763
Michal Vasko96164bf2016-01-21 15:41:58 +0100764int
Michal Vaskob48aa812016-01-18 14:13:09 +0100765nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +0100766{
767 const char *str_type, *str_subtype = NULL, *username;
768 int subtype, type;
769 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +0100770
771 type = ssh_message_type(msg);
772 subtype = ssh_message_subtype(msg);
773
774 switch (type) {
775 case SSH_REQUEST_AUTH:
776 str_type = "request-auth";
777 switch (subtype) {
778 case SSH_AUTH_METHOD_NONE:
779 str_subtype = "none";
780 break;
781 case SSH_AUTH_METHOD_PASSWORD:
782 str_subtype = "password";
783 break;
784 case SSH_AUTH_METHOD_PUBLICKEY:
785 str_subtype = "publickey";
786 break;
787 case SSH_AUTH_METHOD_HOSTBASED:
788 str_subtype = "hostbased";
789 break;
790 case SSH_AUTH_METHOD_INTERACTIVE:
791 str_subtype = "interactive";
792 break;
793 case SSH_AUTH_METHOD_GSSAPI_MIC:
794 str_subtype = "gssapi-mic";
795 break;
796 }
797 break;
798
799 case SSH_REQUEST_CHANNEL_OPEN:
800 str_type = "request-channel-open";
801 switch (subtype) {
802 case SSH_CHANNEL_SESSION:
803 str_subtype = "session";
804 break;
805 case SSH_CHANNEL_DIRECT_TCPIP:
806 str_subtype = "direct-tcpip";
807 break;
808 case SSH_CHANNEL_FORWARDED_TCPIP:
809 str_subtype = "forwarded-tcpip";
810 break;
811 case (int)SSH_CHANNEL_X11:
812 str_subtype = "channel-x11";
813 break;
814 case SSH_CHANNEL_UNKNOWN:
815 /* fallthrough */
816 default:
817 str_subtype = "unknown";
818 break;
819 }
820 break;
821
822 case SSH_REQUEST_CHANNEL:
823 str_type = "request-channel";
824 switch (subtype) {
825 case SSH_CHANNEL_REQUEST_PTY:
826 str_subtype = "pty";
827 break;
828 case SSH_CHANNEL_REQUEST_EXEC:
829 str_subtype = "exec";
830 break;
831 case SSH_CHANNEL_REQUEST_SHELL:
832 str_subtype = "shell";
833 break;
834 case SSH_CHANNEL_REQUEST_ENV:
835 str_subtype = "env";
836 break;
837 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
838 str_subtype = "subsystem";
839 break;
840 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
841 str_subtype = "window-change";
842 break;
843 case SSH_CHANNEL_REQUEST_X11:
844 str_subtype = "x11";
845 break;
846 case SSH_CHANNEL_REQUEST_UNKNOWN:
847 /* fallthrough */
848 default:
849 str_subtype = "unknown";
850 break;
851 }
852 break;
853
854 case SSH_REQUEST_SERVICE:
855 str_type = "request-service";
856 str_subtype = ssh_message_service_service(msg);
857 break;
858
859 case SSH_REQUEST_GLOBAL:
860 str_type = "request-global";
861 switch (subtype) {
862 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
863 str_subtype = "tcpip-forward";
864 break;
865 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
866 str_subtype = "cancel-tcpip-forward";
867 break;
868 case SSH_GLOBAL_REQUEST_UNKNOWN:
869 /* fallthrough */
870 default:
871 str_subtype = "unknown";
872 break;
873 }
874 break;
875
876 default:
877 str_type = "unknown";
878 str_subtype = "unknown";
879 break;
880 }
881
882 VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vaskoce319162016-02-03 15:33:08 +0100883 if ((session->status == NC_STATUS_CLOSING) || (session->status == NC_STATUS_INVALID)) {
884 /* "valid" situation if, for example, receiving some auth or channel request timeouted,
885 * but we got it now, during session free */
886 VRB("SSH message arrived on a %s session, the request will be denied.",
887 (session->status == NC_STATUS_CLOSING ? "closing" : "invalid"));
888 ssh_message_reply_default(msg);
889 return 0;
890 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100891 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +0100892
893 /*
894 * process known messages
895 */
896 if (type == SSH_REQUEST_AUTH) {
897 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
898 ERR("User \"%s\" authenticated, but requested another authentication.", session->username);
899 ssh_message_reply_default(msg);
900 return 0;
901 }
902
Michal Vasko2cc4c682016-03-01 09:16:48 +0100903 if (session->ssh_auth_attempts >= ((struct nc_server_ssh_opts *)session->data)->auth_attempts) {
Michal Vasko086311b2016-01-08 09:53:11 +0100904 /* too many failed attempts */
905 ssh_message_reply_default(msg);
906 return 0;
907 }
908
909 /* save the username, do not let the client change it */
910 username = ssh_message_auth_user(msg);
911 if (!session->username) {
912 if (!username) {
913 ERR("Denying an auth request without a username.");
914 return 1;
915 }
916
Michal Vasko05ba9df2016-01-13 14:40:27 +0100917 session->username = lydict_insert(server_opts.ctx, username, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100918 } else if (username) {
919 if (strcmp(username, session->username)) {
920 ERR("User \"%s\" changed its username to \"%s\".", session->username, username);
921 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +0100922 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +0100923 return 1;
924 }
925 }
926
927 if (subtype == SSH_AUTH_METHOD_NONE) {
928 /* libssh will return the supported auth methods */
929 return 1;
930 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
931 nc_sshcb_auth_password(session, msg);
932 return 0;
933 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
934 nc_sshcb_auth_pubkey(session, msg);
935 return 0;
936 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
937 nc_sshcb_auth_kbdint(session, msg);
938 return 0;
939 }
940 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +0100941 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100942 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100943 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100944 }
Michal Vasko086311b2016-01-08 09:53:11 +0100945 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +0100946
Michal Vasko0df67562016-01-21 15:50:11 +0100947 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100948 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
949 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +0100950 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +0100951 } else {
952 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100953 }
954 return 0;
955 }
956 }
957
958 /* we did not process it */
959 return 1;
960}
961
Michal Vasko1a38c862016-01-15 15:50:07 +0100962/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +0100963static int
964nc_open_netconf_channel(struct nc_session *session, int timeout)
965{
Michal Vasko62be1ce2016-03-03 13:24:52 +0100966 int elapsed_usec = 0, ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100967
968 /* message callback is executed twice to give chance for the channel to be
969 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100970 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +0100971 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100972 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +0100973 return -1;
974 }
975
Michal Vasko62be1ce2016-03-03 13:24:52 +0100976 ret = nc_timedlock(session->ti_lock, timeout);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100977 if (ret != 1) {
978 return ret;
979 }
980
981 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
982 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100983 ERR("Failed to receive SSH messages on a session (%s).",
984 ssh_get_error(session->ti.libssh.session));
Michal Vasko11d142a2016-01-19 15:58:24 +0100985 pthread_mutex_unlock(session->ti_lock);
Michal Vasko086311b2016-01-08 09:53:11 +0100986 return -1;
987 }
988
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100989 if (!session->ti.libssh.channel) {
990 /* we did not receive channel-open, timeout */
991 pthread_mutex_unlock(session->ti_lock);
992 return 0;
993 }
994
995 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
996 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100997 ERR("Failed to receive SSH messages on a session (%s).",
998 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100999 pthread_mutex_unlock(session->ti_lock);
1000 return -1;
1001 }
1002 pthread_mutex_unlock(session->ti_lock);
1003
1004 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1005 /* we did not receive subsystem-request, timeout */
1006 return 0;
1007 }
1008
1009 return 1;
1010 }
1011
1012 while (1) {
1013 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001014 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001015 return -1;
1016 }
1017
Michal Vasko62be1ce2016-03-03 13:24:52 +01001018 ret = nc_timedlock(session->ti_lock, timeout);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001019 if (ret != 1) {
1020 return ret;
1021 }
1022
1023 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1024 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001025 ERR("Failed to receive SSH messages on a session (%s).",
1026 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001027 pthread_mutex_unlock(session->ti_lock);
1028 return -1;
1029 }
1030
1031 pthread_mutex_unlock(session->ti_lock);
1032
Michal Vasko086311b2016-01-08 09:53:11 +01001033 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001034 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001035 }
1036
Michal Vasko105bf272016-02-03 15:34:35 +01001037 if ((timeout != -1) && (elapsed_usec / 1000 >= timeout)) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001038 /* timeout */
Michal Vasko842522c2016-03-01 09:20:13 +01001039 ERR("Failed to start \"netconf\" SSH subsystem for too long, disconnecting.");
Michal Vasko086311b2016-01-08 09:53:11 +01001040 break;
1041 }
1042
Michal Vasko086311b2016-01-08 09:53:11 +01001043 usleep(NC_TIMEOUT_STEP);
Michal Vasko105bf272016-02-03 15:34:35 +01001044 elapsed_usec += NC_TIMEOUT_STEP;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001045 }
Michal Vasko086311b2016-01-08 09:53:11 +01001046
Michal Vasko1a38c862016-01-15 15:50:07 +01001047 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001048}
1049
Michal Vasko96164bf2016-01-21 15:41:58 +01001050int
Michal Vasko62be1ce2016-03-03 13:24:52 +01001051nc_ssh_pollin(struct nc_session *session, int timeout)
Michal Vasko96164bf2016-01-21 15:41:58 +01001052{
Michal Vasko62be1ce2016-03-03 13:24:52 +01001053 int ret;
Michal Vasko96164bf2016-01-21 15:41:58 +01001054 struct nc_session *new;
1055
Michal Vasko62be1ce2016-03-03 13:24:52 +01001056 ret = nc_timedlock(session->ti_lock, timeout);
Michal Vasko96164bf2016-01-21 15:41:58 +01001057
Michal Vasko71090fc2016-05-24 16:37:28 +02001058 if (ret < 0) {
1059 return NC_PSPOLL_ERROR;
1060 } else if (!ret) {
1061 return NC_PSPOLL_TIMEOUT;
Michal Vasko96164bf2016-01-21 15:41:58 +01001062 }
1063
1064 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1065 pthread_mutex_unlock(session->ti_lock);
1066
1067 if (ret != SSH_OK) {
1068 ERR("Session %u: failed to receive SSH messages (%s).", session->id,
1069 ssh_get_error(session->ti.libssh.session));
1070 session->status = NC_STATUS_INVALID;
1071 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko71090fc2016-05-24 16:37:28 +02001072 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001073 }
1074
1075 /* new SSH message */
1076 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1077 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1078 if (session->ti.libssh.next) {
1079 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1080 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1081 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1082 /* new NETCONF SSH channel */
Michal Vasko71090fc2016-05-24 16:37:28 +02001083 return NC_PSPOLL_SSH_CHANNEL;
Michal Vasko96164bf2016-01-21 15:41:58 +01001084 }
1085 }
1086 }
1087
1088 /* just some SSH message */
Michal Vasko71090fc2016-05-24 16:37:28 +02001089 return NC_PSPOLL_SSH_MSG;
Michal Vasko96164bf2016-01-21 15:41:58 +01001090 }
1091
1092 /* no new SSH message, maybe NETCONF data? */
1093 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
1094 /* not this one */
1095 if (!ret) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001096 return NC_PSPOLL_PENDING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001097 } else if (ret == SSH_ERROR) {
1098 ERR("Session %u: SSH channel error (%s).", session->id,
1099 ssh_get_error(session->ti.libssh.session));
1100 session->status = NC_STATUS_INVALID;
1101 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko71090fc2016-05-24 16:37:28 +02001102 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001103 } else if (ret == SSH_EOF) {
1104 ERR("Session %u: communication channel unexpectedly closed (libssh).",
1105 session->id);
1106 session->status = NC_STATUS_INVALID;
1107 session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko71090fc2016-05-24 16:37:28 +02001108 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001109 }
1110
Michal Vasko71090fc2016-05-24 16:37:28 +02001111 return NC_PSPOLL_RPC;
Michal Vasko96164bf2016-01-21 15:41:58 +01001112}
1113
Michal Vasko71090fc2016-05-24 16:37:28 +02001114API NC_MSG_TYPE
Michal Vasko8f5270d2016-02-29 16:22:25 +01001115nc_connect_callhome_ssh(const char *host, uint16_t port, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001116{
Michal Vasko8f5270d2016-02-29 16:22:25 +01001117 return nc_connect_callhome(host, port, NC_TI_LIBSSH, session);
Michal Vasko3031aae2016-01-27 16:07:18 +01001118}
1119
1120int
Michal Vasko0190bc32016-03-02 15:47:49 +01001121nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko3031aae2016-01-27 16:07:18 +01001122{
Michal Vaskoe2713da2016-08-22 16:06:40 +02001123 ssh_bind sbind;
Michal Vasko3031aae2016-01-27 16:07:18 +01001124 struct nc_server_ssh_opts *opts;
Michal Vasko72387da2016-02-02 15:52:41 +01001125 int libssh_auth_methods = 0, elapsed_usec = 0, ret;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001126 uint8_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001127
Michal Vasko2cc4c682016-03-01 09:16:48 +01001128 opts = session->data;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001129
Michal Vasko086311b2016-01-08 09:53:11 +01001130 /* other transport-specific data */
1131 session->ti_type = NC_TI_LIBSSH;
1132 session->ti.libssh.session = ssh_new();
1133 if (!session->ti.libssh.session) {
Michal Vaskod083db62016-01-19 10:31:29 +01001134 ERR("Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001135 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001136 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001137 }
1138
Michal Vaskoc61c4492016-01-25 11:13:34 +01001139 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001140 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1141 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001142 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001143 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1144 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001145 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001146 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1147 }
1148 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1149
Michal Vaskoe2713da2016-08-22 16:06:40 +02001150 sbind = ssh_bind_new();
1151 if (!sbind) {
1152 ERR("Failed to create an SSH bind.");
1153 close(sock);
1154 return -1;
1155 }
1156 for (i = 0; i < opts->hostkey_count; ++i) {
1157 if (ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_HOSTKEY, opts->hostkeys[i]) != SSH_OK) {
1158 ERR("Failed to set hostkey \"%s\" (%s).", opts->hostkeys[i], ssh_get_error(sbind));
1159 close(sock);
1160 ssh_bind_free(sbind);
1161 return -1;
1162 }
1163 }
1164 if (opts->banner) {
1165 ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_BANNER, opts->banner);
1166 }
1167
Michal Vasko086311b2016-01-08 09:53:11 +01001168 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001169 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001170 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001171
Michal Vaskoe2713da2016-08-22 16:06:40 +02001172 if (ssh_bind_accept_fd(sbind, session->ti.libssh.session, sock) == SSH_ERROR) {
1173 ERR("SSH failed to accept a new connection (%s).", ssh_get_error(sbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001174 close(sock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001175 ssh_bind_free(sbind);
Michal Vasko9e036d52016-01-08 10:49:26 +01001176 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001177 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001178 ssh_bind_free(sbind);
Michal Vasko086311b2016-01-08 09:53:11 +01001179
Michal Vasko0190bc32016-03-02 15:47:49 +01001180 ssh_set_blocking(session->ti.libssh.session, 0);
1181
1182 while ((ret = ssh_handle_key_exchange(session->ti.libssh.session)) == SSH_AGAIN) {
Michal Vaskoe65b4492016-03-03 11:57:51 +01001183 /* this tends to take longer */
1184 usleep(NC_TIMEOUT_STEP * 20);
1185 elapsed_usec += NC_TIMEOUT_STEP * 20;
Michal Vasko0190bc32016-03-02 15:47:49 +01001186 if ((timeout > -1) && (elapsed_usec / 1000 >= timeout)) {
1187 break;
1188 }
1189 }
1190 if (ret == SSH_AGAIN) {
1191 ERR("SSH key exchange timeout.");
1192 return 0;
1193 } else if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001194 ERR("SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001195 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001196 }
1197
1198 /* authenticate */
Michal Vasko0190bc32016-03-02 15:47:49 +01001199 elapsed_usec = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001200 do {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001201 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001202 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001203 return -1;
1204 }
1205
Michal Vasko086311b2016-01-08 09:53:11 +01001206 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001207 ERR("Failed to receive SSH messages on a session (%s).",
1208 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001209 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001210 }
1211
1212 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1213 break;
1214 }
1215
1216 usleep(NC_TIMEOUT_STEP);
Michal Vasko72387da2016-02-02 15:52:41 +01001217 elapsed_usec += NC_TIMEOUT_STEP;
Michal Vaskoab639942016-02-29 16:23:47 +01001218 } while (!opts->auth_timeout || (elapsed_usec / 1000000 < opts->auth_timeout));
Michal Vasko086311b2016-01-08 09:53:11 +01001219
1220 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1221 /* timeout */
Michal Vasko842522c2016-03-01 09:20:13 +01001222 ERR("Client failed to authenticate for too long, disconnecting.");
Michal Vasko1a38c862016-01-15 15:50:07 +01001223 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001224 }
1225
Michal Vasko086311b2016-01-08 09:53:11 +01001226 /* open channel */
Michal Vaskoab639942016-02-29 16:23:47 +01001227 ret = nc_open_netconf_channel(session, opts->auth_timeout ? (opts->auth_timeout * 1000 - elapsed_usec / 1000) : -1);
Michal Vasko1a38c862016-01-15 15:50:07 +01001228 if (ret < 1) {
1229 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001230 }
1231
Michal Vasko96164bf2016-01-21 15:41:58 +01001232 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
Michal Vasko1a38c862016-01-15 15:50:07 +01001233 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001234}
1235
Michal Vasko71090fc2016-05-24 16:37:28 +02001236API NC_MSG_TYPE
1237nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session **session)
1238{
1239 NC_MSG_TYPE msgtype;
1240 struct nc_session *new_session = NULL;
1241
1242 if (!orig_session) {
1243 ERRARG("orig_session");
1244 return NC_MSG_ERROR;
1245 } else if (!session) {
1246 ERRARG("session");
1247 return NC_MSG_ERROR;
1248 }
1249
1250 if ((orig_session->status == NC_STATUS_RUNNING) && (orig_session->ti_type == NC_TI_LIBSSH)
1251 && orig_session->ti.libssh.next) {
1252 for (new_session = orig_session->ti.libssh.next;
1253 new_session != orig_session;
1254 new_session = new_session->ti.libssh.next) {
1255 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1256 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1257 /* we found our session */
1258 break;
1259 }
1260 }
1261 if (new_session == orig_session) {
1262 new_session = NULL;
1263 }
1264 }
1265
1266 if (!new_session) {
1267 ERR("Session does not have a NETCONF SSH channel ready.");
1268 return NC_MSG_ERROR;
1269 }
1270
1271 /* assign new SID atomically */
1272 pthread_spin_lock(&server_opts.sid_lock);
1273 new_session->id = server_opts.new_session_id++;
1274 pthread_spin_unlock(&server_opts.sid_lock);
1275
1276 /* NETCONF handshake */
1277 msgtype = nc_handshake(new_session);
1278 if (msgtype != NC_MSG_HELLO) {
1279 return msgtype;
1280 }
1281
Michal Vasko2f975cf2016-07-28 15:47:00 +02001282 new_session->session_start = new_session->last_rpc = time(NULL);
Michal Vasko71090fc2016-05-24 16:37:28 +02001283 new_session->status = NC_STATUS_RUNNING;
1284 *session = new_session;
1285
1286 return msgtype;
1287}
1288
1289API NC_MSG_TYPE
Michal Vasko96164bf2016-01-21 15:41:58 +01001290nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001291{
Michal Vaskobdcf2362016-07-26 11:35:43 +02001292 uint8_t q_id;
Michal Vasko71090fc2016-05-24 16:37:28 +02001293 NC_MSG_TYPE msgtype;
Michal Vasko96164bf2016-01-21 15:41:58 +01001294 struct nc_session *new_session = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001295 uint16_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001296
Michal Vasko45e53ae2016-04-07 11:46:03 +02001297 if (!ps) {
1298 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001299 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001300 } else if (!session) {
1301 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001302 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01001303 }
1304
Michal Vasko48a63ed2016-03-01 09:48:21 +01001305 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001306 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001307 return NC_MSG_ERROR;
Michal Vaskof04a52a2016-04-07 10:52:10 +02001308 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001309
Michal Vasko96164bf2016-01-21 15:41:58 +01001310 for (i = 0; i < ps->session_count; ++i) {
1311 if ((ps->sessions[i]->status == NC_STATUS_RUNNING) && (ps->sessions[i]->ti_type == NC_TI_LIBSSH)
1312 && ps->sessions[i]->ti.libssh.next) {
1313 /* an SSH session with more channels */
1314 for (new_session = ps->sessions[i]->ti.libssh.next;
1315 new_session != ps->sessions[i];
1316 new_session = new_session->ti.libssh.next) {
1317 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1318 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1319 /* we found our session */
1320 break;
1321 }
1322 }
1323 if (new_session != ps->sessions[i]) {
1324 break;
1325 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001326
Michal Vasko96164bf2016-01-21 15:41:58 +01001327 new_session = NULL;
1328 }
1329 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001330
Michal Vasko48a63ed2016-03-01 09:48:21 +01001331 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001332 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001333
Michal Vasko96164bf2016-01-21 15:41:58 +01001334 if (!new_session) {
1335 ERR("No session with a NETCONF SSH channel ready was found.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001336 return NC_MSG_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001337 }
1338
1339 /* assign new SID atomically */
1340 pthread_spin_lock(&server_opts.sid_lock);
1341 new_session->id = server_opts.new_session_id++;
1342 pthread_spin_unlock(&server_opts.sid_lock);
Michal Vaskofb89d772016-01-08 12:25:35 +01001343
Michal Vasko086311b2016-01-08 09:53:11 +01001344 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001345 msgtype = nc_handshake(new_session);
1346 if (msgtype != NC_MSG_HELLO) {
1347 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001348 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001349
Michal Vasko2f975cf2016-07-28 15:47:00 +02001350 new_session->session_start = new_session->last_rpc = time(NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001351 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001352 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001353
Michal Vasko71090fc2016-05-24 16:37:28 +02001354 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001355}