blob: 74516aaaab7f7c7a74e55c0d12c60d0d18b16cd7 [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
Michal Vaskobd13a932016-09-14 09:00:35 +0200652 if ((username = auth_pubkey_compare_key(session->data, ssh_message_auth_pubkey(msg))) == NULL) {
653 VRB("User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
654 goto fail;
655 } else if (strcmp(session->username, username)) {
656 VRB("User \"%s\" is not the username identified with the presented public key.", session->username);
657 free(username);
658 goto fail;
659 }
660 free(username);
661
Michal Vasko086311b2016-01-08 09:53:11 +0100662 signature_state = ssh_message_auth_publickey_state(msg);
663 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
664 VRB("User \"%s\" authenticated.", session->username);
665 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
666 ssh_message_auth_reply_success(msg, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100667 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Michal Vaskobd13a932016-09-14 09:00:35 +0200668 /* accepting only the use of a public key */
669 ssh_message_auth_reply_pk_ok_simple(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100670 }
671
Michal Vaskobd13a932016-09-14 09:00:35 +0200672 return;
673
674fail:
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100675 ++session->ssh_auth_attempts;
676 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100677 ssh_message_reply_default(msg);
678}
679
680static int
Michal Vasko96164bf2016-01-21 15:41:58 +0100681nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100682{
Michal Vasko96164bf2016-01-21 15:41:58 +0100683 ssh_channel chan;
684
685 /* first channel request */
686 if (!session->ti.libssh.channel) {
687 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100688 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100689 return -1;
690 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100691 chan = ssh_message_channel_request_open_reply_accept(msg);
692 if (!chan) {
693 ERR("Failed to create a new SSH channel.");
694 return -1;
695 }
696 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +0100697
Michal Vasko96164bf2016-01-21 15:41:58 +0100698 /* additional channel request */
699 } else {
700 chan = ssh_message_channel_request_open_reply_accept(msg);
701 if (!chan) {
702 ERR("Session %u: failed to create a new SSH channel.", session->id);
703 return -1;
704 }
705 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +0100706 }
707
Michal Vasko086311b2016-01-08 09:53:11 +0100708 return 0;
709}
710
711static int
712nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
713{
Michal Vasko96164bf2016-01-21 15:41:58 +0100714 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +0100715
Michal Vasko96164bf2016-01-21 15:41:58 +0100716 if (strcmp(subsystem, "netconf")) {
717 WRN("Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +0100718 return -1;
719 }
720
Michal Vasko96164bf2016-01-21 15:41:58 +0100721 if (session->ti.libssh.channel == channel) {
722 /* first channel requested */
723 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
724 ERRINT;
725 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100726 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100727 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
728 ERR("Session %u: subsystem \"netconf\" requested for the second time.", session->id);
729 return -1;
730 }
731
732 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +0100733 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +0100734 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
735 new_session = calloc(1, sizeof *new_session);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100736 if (!new_session) {
737 ERRMEM;
738 return -1;
739 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100740
741 /* insert the new session */
742 if (!session->ti.libssh.next) {
743 new_session->ti.libssh.next = session;
744 } else {
745 new_session->ti.libssh.next = session->ti.libssh.next;
746 }
747 session->ti.libssh.next = new_session;
748
749 new_session->status = NC_STATUS_STARTING;
750 new_session->side = NC_SERVER;
751 new_session->ti_type = NC_TI_LIBSSH;
752 new_session->ti_lock = session->ti_lock;
753 new_session->ti.libssh.channel = channel;
754 new_session->ti.libssh.session = session->ti.libssh.session;
755 new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
756 new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
757 new_session->port = session->port;
758 new_session->ctx = server_opts.ctx;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100759 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX
760 | (session->flags & NC_SESSION_CALLHOME ? NC_SESSION_CALLHOME : 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100761 }
762
763 return 0;
764}
765
Michal Vasko96164bf2016-01-21 15:41:58 +0100766int
Michal Vaskob48aa812016-01-18 14:13:09 +0100767nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +0100768{
769 const char *str_type, *str_subtype = NULL, *username;
770 int subtype, type;
771 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +0100772
773 type = ssh_message_type(msg);
774 subtype = ssh_message_subtype(msg);
775
776 switch (type) {
777 case SSH_REQUEST_AUTH:
778 str_type = "request-auth";
779 switch (subtype) {
780 case SSH_AUTH_METHOD_NONE:
781 str_subtype = "none";
782 break;
783 case SSH_AUTH_METHOD_PASSWORD:
784 str_subtype = "password";
785 break;
786 case SSH_AUTH_METHOD_PUBLICKEY:
787 str_subtype = "publickey";
788 break;
789 case SSH_AUTH_METHOD_HOSTBASED:
790 str_subtype = "hostbased";
791 break;
792 case SSH_AUTH_METHOD_INTERACTIVE:
793 str_subtype = "interactive";
794 break;
795 case SSH_AUTH_METHOD_GSSAPI_MIC:
796 str_subtype = "gssapi-mic";
797 break;
798 }
799 break;
800
801 case SSH_REQUEST_CHANNEL_OPEN:
802 str_type = "request-channel-open";
803 switch (subtype) {
804 case SSH_CHANNEL_SESSION:
805 str_subtype = "session";
806 break;
807 case SSH_CHANNEL_DIRECT_TCPIP:
808 str_subtype = "direct-tcpip";
809 break;
810 case SSH_CHANNEL_FORWARDED_TCPIP:
811 str_subtype = "forwarded-tcpip";
812 break;
813 case (int)SSH_CHANNEL_X11:
814 str_subtype = "channel-x11";
815 break;
816 case SSH_CHANNEL_UNKNOWN:
817 /* fallthrough */
818 default:
819 str_subtype = "unknown";
820 break;
821 }
822 break;
823
824 case SSH_REQUEST_CHANNEL:
825 str_type = "request-channel";
826 switch (subtype) {
827 case SSH_CHANNEL_REQUEST_PTY:
828 str_subtype = "pty";
829 break;
830 case SSH_CHANNEL_REQUEST_EXEC:
831 str_subtype = "exec";
832 break;
833 case SSH_CHANNEL_REQUEST_SHELL:
834 str_subtype = "shell";
835 break;
836 case SSH_CHANNEL_REQUEST_ENV:
837 str_subtype = "env";
838 break;
839 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
840 str_subtype = "subsystem";
841 break;
842 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
843 str_subtype = "window-change";
844 break;
845 case SSH_CHANNEL_REQUEST_X11:
846 str_subtype = "x11";
847 break;
848 case SSH_CHANNEL_REQUEST_UNKNOWN:
849 /* fallthrough */
850 default:
851 str_subtype = "unknown";
852 break;
853 }
854 break;
855
856 case SSH_REQUEST_SERVICE:
857 str_type = "request-service";
858 str_subtype = ssh_message_service_service(msg);
859 break;
860
861 case SSH_REQUEST_GLOBAL:
862 str_type = "request-global";
863 switch (subtype) {
864 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
865 str_subtype = "tcpip-forward";
866 break;
867 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
868 str_subtype = "cancel-tcpip-forward";
869 break;
870 case SSH_GLOBAL_REQUEST_UNKNOWN:
871 /* fallthrough */
872 default:
873 str_subtype = "unknown";
874 break;
875 }
876 break;
877
878 default:
879 str_type = "unknown";
880 str_subtype = "unknown";
881 break;
882 }
883
884 VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vaskoce319162016-02-03 15:33:08 +0100885 if ((session->status == NC_STATUS_CLOSING) || (session->status == NC_STATUS_INVALID)) {
886 /* "valid" situation if, for example, receiving some auth or channel request timeouted,
887 * but we got it now, during session free */
888 VRB("SSH message arrived on a %s session, the request will be denied.",
889 (session->status == NC_STATUS_CLOSING ? "closing" : "invalid"));
890 ssh_message_reply_default(msg);
891 return 0;
892 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100893 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +0100894
895 /*
896 * process known messages
897 */
898 if (type == SSH_REQUEST_AUTH) {
899 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
900 ERR("User \"%s\" authenticated, but requested another authentication.", session->username);
901 ssh_message_reply_default(msg);
902 return 0;
903 }
904
Michal Vasko2cc4c682016-03-01 09:16:48 +0100905 if (session->ssh_auth_attempts >= ((struct nc_server_ssh_opts *)session->data)->auth_attempts) {
Michal Vasko086311b2016-01-08 09:53:11 +0100906 /* too many failed attempts */
907 ssh_message_reply_default(msg);
908 return 0;
909 }
910
911 /* save the username, do not let the client change it */
912 username = ssh_message_auth_user(msg);
913 if (!session->username) {
914 if (!username) {
915 ERR("Denying an auth request without a username.");
916 return 1;
917 }
918
Michal Vasko05ba9df2016-01-13 14:40:27 +0100919 session->username = lydict_insert(server_opts.ctx, username, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100920 } else if (username) {
921 if (strcmp(username, session->username)) {
922 ERR("User \"%s\" changed its username to \"%s\".", session->username, username);
923 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +0100924 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +0100925 return 1;
926 }
927 }
928
929 if (subtype == SSH_AUTH_METHOD_NONE) {
930 /* libssh will return the supported auth methods */
931 return 1;
932 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
933 nc_sshcb_auth_password(session, msg);
934 return 0;
935 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
936 nc_sshcb_auth_pubkey(session, msg);
937 return 0;
938 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
939 nc_sshcb_auth_kbdint(session, msg);
940 return 0;
941 }
942 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +0100943 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100944 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100945 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100946 }
Michal Vasko086311b2016-01-08 09:53:11 +0100947 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +0100948
Michal Vasko0df67562016-01-21 15:50:11 +0100949 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100950 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
951 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +0100952 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +0100953 } else {
954 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100955 }
956 return 0;
957 }
958 }
959
960 /* we did not process it */
961 return 1;
962}
963
Michal Vasko1a38c862016-01-15 15:50:07 +0100964/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +0100965static int
966nc_open_netconf_channel(struct nc_session *session, int timeout)
967{
Michal Vasko62be1ce2016-03-03 13:24:52 +0100968 int elapsed_usec = 0, ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100969
970 /* message callback is executed twice to give chance for the channel to be
971 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100972 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +0100973 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100974 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +0100975 return -1;
976 }
977
Michal Vasko62be1ce2016-03-03 13:24:52 +0100978 ret = nc_timedlock(session->ti_lock, timeout);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100979 if (ret != 1) {
980 return ret;
981 }
982
983 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
984 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100985 ERR("Failed to receive SSH messages on a session (%s).",
986 ssh_get_error(session->ti.libssh.session));
Michal Vasko11d142a2016-01-19 15:58:24 +0100987 pthread_mutex_unlock(session->ti_lock);
Michal Vasko086311b2016-01-08 09:53:11 +0100988 return -1;
989 }
990
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100991 if (!session->ti.libssh.channel) {
992 /* we did not receive channel-open, timeout */
993 pthread_mutex_unlock(session->ti_lock);
994 return 0;
995 }
996
997 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
998 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100999 ERR("Failed to receive SSH messages on a session (%s).",
1000 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001001 pthread_mutex_unlock(session->ti_lock);
1002 return -1;
1003 }
1004 pthread_mutex_unlock(session->ti_lock);
1005
1006 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1007 /* we did not receive subsystem-request, timeout */
1008 return 0;
1009 }
1010
1011 return 1;
1012 }
1013
1014 while (1) {
1015 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001016 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001017 return -1;
1018 }
1019
Michal Vasko62be1ce2016-03-03 13:24:52 +01001020 ret = nc_timedlock(session->ti_lock, timeout);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001021 if (ret != 1) {
1022 return ret;
1023 }
1024
1025 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1026 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001027 ERR("Failed to receive SSH messages on a session (%s).",
1028 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001029 pthread_mutex_unlock(session->ti_lock);
1030 return -1;
1031 }
1032
1033 pthread_mutex_unlock(session->ti_lock);
1034
Michal Vasko086311b2016-01-08 09:53:11 +01001035 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001036 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001037 }
1038
Michal Vasko105bf272016-02-03 15:34:35 +01001039 if ((timeout != -1) && (elapsed_usec / 1000 >= timeout)) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001040 /* timeout */
Michal Vasko842522c2016-03-01 09:20:13 +01001041 ERR("Failed to start \"netconf\" SSH subsystem for too long, disconnecting.");
Michal Vasko086311b2016-01-08 09:53:11 +01001042 break;
1043 }
1044
Michal Vasko086311b2016-01-08 09:53:11 +01001045 usleep(NC_TIMEOUT_STEP);
Michal Vasko105bf272016-02-03 15:34:35 +01001046 elapsed_usec += NC_TIMEOUT_STEP;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001047 }
Michal Vasko086311b2016-01-08 09:53:11 +01001048
Michal Vasko1a38c862016-01-15 15:50:07 +01001049 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001050}
1051
Michal Vasko96164bf2016-01-21 15:41:58 +01001052int
Michal Vasko62be1ce2016-03-03 13:24:52 +01001053nc_ssh_pollin(struct nc_session *session, int timeout)
Michal Vasko96164bf2016-01-21 15:41:58 +01001054{
Michal Vasko62be1ce2016-03-03 13:24:52 +01001055 int ret;
Michal Vasko96164bf2016-01-21 15:41:58 +01001056 struct nc_session *new;
1057
Michal Vasko62be1ce2016-03-03 13:24:52 +01001058 ret = nc_timedlock(session->ti_lock, timeout);
Michal Vasko96164bf2016-01-21 15:41:58 +01001059
Michal Vasko71090fc2016-05-24 16:37:28 +02001060 if (ret < 0) {
1061 return NC_PSPOLL_ERROR;
1062 } else if (!ret) {
1063 return NC_PSPOLL_TIMEOUT;
Michal Vasko96164bf2016-01-21 15:41:58 +01001064 }
1065
1066 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1067 pthread_mutex_unlock(session->ti_lock);
1068
1069 if (ret != SSH_OK) {
1070 ERR("Session %u: failed to receive SSH messages (%s).", session->id,
1071 ssh_get_error(session->ti.libssh.session));
1072 session->status = NC_STATUS_INVALID;
1073 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko71090fc2016-05-24 16:37:28 +02001074 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001075 }
1076
1077 /* new SSH message */
1078 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1079 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1080 if (session->ti.libssh.next) {
1081 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1082 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1083 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1084 /* new NETCONF SSH channel */
Michal Vasko71090fc2016-05-24 16:37:28 +02001085 return NC_PSPOLL_SSH_CHANNEL;
Michal Vasko96164bf2016-01-21 15:41:58 +01001086 }
1087 }
1088 }
1089
1090 /* just some SSH message */
Michal Vasko71090fc2016-05-24 16:37:28 +02001091 return NC_PSPOLL_SSH_MSG;
Michal Vasko96164bf2016-01-21 15:41:58 +01001092 }
1093
1094 /* no new SSH message, maybe NETCONF data? */
1095 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
1096 /* not this one */
1097 if (!ret) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001098 return NC_PSPOLL_PENDING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001099 } else if (ret == SSH_ERROR) {
1100 ERR("Session %u: SSH channel error (%s).", session->id,
1101 ssh_get_error(session->ti.libssh.session));
1102 session->status = NC_STATUS_INVALID;
1103 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko71090fc2016-05-24 16:37:28 +02001104 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001105 } else if (ret == SSH_EOF) {
1106 ERR("Session %u: communication channel unexpectedly closed (libssh).",
1107 session->id);
1108 session->status = NC_STATUS_INVALID;
1109 session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko71090fc2016-05-24 16:37:28 +02001110 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001111 }
1112
Michal Vasko71090fc2016-05-24 16:37:28 +02001113 return NC_PSPOLL_RPC;
Michal Vasko96164bf2016-01-21 15:41:58 +01001114}
1115
Michal Vasko71090fc2016-05-24 16:37:28 +02001116API NC_MSG_TYPE
Michal Vasko8f5270d2016-02-29 16:22:25 +01001117nc_connect_callhome_ssh(const char *host, uint16_t port, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001118{
Michal Vasko8f5270d2016-02-29 16:22:25 +01001119 return nc_connect_callhome(host, port, NC_TI_LIBSSH, session);
Michal Vasko3031aae2016-01-27 16:07:18 +01001120}
1121
1122int
Michal Vasko0190bc32016-03-02 15:47:49 +01001123nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko3031aae2016-01-27 16:07:18 +01001124{
Michal Vaskoe2713da2016-08-22 16:06:40 +02001125 ssh_bind sbind;
Michal Vasko3031aae2016-01-27 16:07:18 +01001126 struct nc_server_ssh_opts *opts;
Michal Vasko72387da2016-02-02 15:52:41 +01001127 int libssh_auth_methods = 0, elapsed_usec = 0, ret;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001128 uint8_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001129
Michal Vasko2cc4c682016-03-01 09:16:48 +01001130 opts = session->data;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001131
Michal Vasko086311b2016-01-08 09:53:11 +01001132 /* other transport-specific data */
1133 session->ti_type = NC_TI_LIBSSH;
1134 session->ti.libssh.session = ssh_new();
1135 if (!session->ti.libssh.session) {
Michal Vaskod083db62016-01-19 10:31:29 +01001136 ERR("Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001137 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001138 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001139 }
1140
Michal Vaskoc61c4492016-01-25 11:13:34 +01001141 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001142 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1143 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001144 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001145 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1146 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001147 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001148 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1149 }
1150 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1151
Michal Vaskoe2713da2016-08-22 16:06:40 +02001152 sbind = ssh_bind_new();
1153 if (!sbind) {
1154 ERR("Failed to create an SSH bind.");
1155 close(sock);
1156 return -1;
1157 }
1158 for (i = 0; i < opts->hostkey_count; ++i) {
1159 if (ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_HOSTKEY, opts->hostkeys[i]) != SSH_OK) {
1160 ERR("Failed to set hostkey \"%s\" (%s).", opts->hostkeys[i], ssh_get_error(sbind));
1161 close(sock);
1162 ssh_bind_free(sbind);
1163 return -1;
1164 }
1165 }
1166 if (opts->banner) {
1167 ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_BANNER, opts->banner);
1168 }
1169
Michal Vasko086311b2016-01-08 09:53:11 +01001170 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001171 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001172 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001173
Michal Vaskoe2713da2016-08-22 16:06:40 +02001174 if (ssh_bind_accept_fd(sbind, session->ti.libssh.session, sock) == SSH_ERROR) {
1175 ERR("SSH failed to accept a new connection (%s).", ssh_get_error(sbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001176 close(sock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001177 ssh_bind_free(sbind);
Michal Vasko9e036d52016-01-08 10:49:26 +01001178 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001179 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001180 ssh_bind_free(sbind);
Michal Vasko086311b2016-01-08 09:53:11 +01001181
Michal Vasko0190bc32016-03-02 15:47:49 +01001182 ssh_set_blocking(session->ti.libssh.session, 0);
1183
1184 while ((ret = ssh_handle_key_exchange(session->ti.libssh.session)) == SSH_AGAIN) {
Michal Vaskoe65b4492016-03-03 11:57:51 +01001185 /* this tends to take longer */
1186 usleep(NC_TIMEOUT_STEP * 20);
1187 elapsed_usec += NC_TIMEOUT_STEP * 20;
Michal Vasko0190bc32016-03-02 15:47:49 +01001188 if ((timeout > -1) && (elapsed_usec / 1000 >= timeout)) {
1189 break;
1190 }
1191 }
1192 if (ret == SSH_AGAIN) {
1193 ERR("SSH key exchange timeout.");
1194 return 0;
1195 } else if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001196 ERR("SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001197 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001198 }
1199
1200 /* authenticate */
Michal Vasko0190bc32016-03-02 15:47:49 +01001201 elapsed_usec = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001202 do {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001203 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001204 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001205 return -1;
1206 }
1207
Michal Vasko086311b2016-01-08 09:53:11 +01001208 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001209 ERR("Failed to receive SSH messages on a session (%s).",
1210 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001211 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001212 }
1213
1214 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1215 break;
1216 }
1217
1218 usleep(NC_TIMEOUT_STEP);
Michal Vasko72387da2016-02-02 15:52:41 +01001219 elapsed_usec += NC_TIMEOUT_STEP;
Michal Vaskoab639942016-02-29 16:23:47 +01001220 } while (!opts->auth_timeout || (elapsed_usec / 1000000 < opts->auth_timeout));
Michal Vasko086311b2016-01-08 09:53:11 +01001221
1222 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1223 /* timeout */
Michal Vasko842522c2016-03-01 09:20:13 +01001224 ERR("Client failed to authenticate for too long, disconnecting.");
Michal Vasko1a38c862016-01-15 15:50:07 +01001225 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001226 }
1227
Michal Vasko086311b2016-01-08 09:53:11 +01001228 /* open channel */
Michal Vaskoab639942016-02-29 16:23:47 +01001229 ret = nc_open_netconf_channel(session, opts->auth_timeout ? (opts->auth_timeout * 1000 - elapsed_usec / 1000) : -1);
Michal Vasko1a38c862016-01-15 15:50:07 +01001230 if (ret < 1) {
1231 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001232 }
1233
Michal Vasko96164bf2016-01-21 15:41:58 +01001234 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
Michal Vasko1a38c862016-01-15 15:50:07 +01001235 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001236}
1237
Michal Vasko71090fc2016-05-24 16:37:28 +02001238API NC_MSG_TYPE
1239nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session **session)
1240{
1241 NC_MSG_TYPE msgtype;
1242 struct nc_session *new_session = NULL;
1243
1244 if (!orig_session) {
1245 ERRARG("orig_session");
1246 return NC_MSG_ERROR;
1247 } else if (!session) {
1248 ERRARG("session");
1249 return NC_MSG_ERROR;
1250 }
1251
1252 if ((orig_session->status == NC_STATUS_RUNNING) && (orig_session->ti_type == NC_TI_LIBSSH)
1253 && orig_session->ti.libssh.next) {
1254 for (new_session = orig_session->ti.libssh.next;
1255 new_session != orig_session;
1256 new_session = new_session->ti.libssh.next) {
1257 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1258 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1259 /* we found our session */
1260 break;
1261 }
1262 }
1263 if (new_session == orig_session) {
1264 new_session = NULL;
1265 }
1266 }
1267
1268 if (!new_session) {
1269 ERR("Session does not have a NETCONF SSH channel ready.");
1270 return NC_MSG_ERROR;
1271 }
1272
1273 /* assign new SID atomically */
1274 pthread_spin_lock(&server_opts.sid_lock);
1275 new_session->id = server_opts.new_session_id++;
1276 pthread_spin_unlock(&server_opts.sid_lock);
1277
1278 /* NETCONF handshake */
1279 msgtype = nc_handshake(new_session);
1280 if (msgtype != NC_MSG_HELLO) {
1281 return msgtype;
1282 }
1283
Michal Vasko2f975cf2016-07-28 15:47:00 +02001284 new_session->session_start = new_session->last_rpc = time(NULL);
Michal Vasko71090fc2016-05-24 16:37:28 +02001285 new_session->status = NC_STATUS_RUNNING;
1286 *session = new_session;
1287
1288 return msgtype;
1289}
1290
1291API NC_MSG_TYPE
Michal Vasko96164bf2016-01-21 15:41:58 +01001292nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001293{
Michal Vaskobdcf2362016-07-26 11:35:43 +02001294 uint8_t q_id;
Michal Vasko71090fc2016-05-24 16:37:28 +02001295 NC_MSG_TYPE msgtype;
Michal Vasko96164bf2016-01-21 15:41:58 +01001296 struct nc_session *new_session = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001297 uint16_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001298
Michal Vasko45e53ae2016-04-07 11:46:03 +02001299 if (!ps) {
1300 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001301 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001302 } else if (!session) {
1303 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001304 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01001305 }
1306
Michal Vasko48a63ed2016-03-01 09:48:21 +01001307 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001308 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001309 return NC_MSG_ERROR;
Michal Vaskof04a52a2016-04-07 10:52:10 +02001310 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001311
Michal Vasko96164bf2016-01-21 15:41:58 +01001312 for (i = 0; i < ps->session_count; ++i) {
1313 if ((ps->sessions[i]->status == NC_STATUS_RUNNING) && (ps->sessions[i]->ti_type == NC_TI_LIBSSH)
1314 && ps->sessions[i]->ti.libssh.next) {
1315 /* an SSH session with more channels */
1316 for (new_session = ps->sessions[i]->ti.libssh.next;
1317 new_session != ps->sessions[i];
1318 new_session = new_session->ti.libssh.next) {
1319 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1320 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1321 /* we found our session */
1322 break;
1323 }
1324 }
1325 if (new_session != ps->sessions[i]) {
1326 break;
1327 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001328
Michal Vasko96164bf2016-01-21 15:41:58 +01001329 new_session = NULL;
1330 }
1331 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001332
Michal Vasko48a63ed2016-03-01 09:48:21 +01001333 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001334 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001335
Michal Vasko96164bf2016-01-21 15:41:58 +01001336 if (!new_session) {
1337 ERR("No session with a NETCONF SSH channel ready was found.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001338 return NC_MSG_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001339 }
1340
1341 /* assign new SID atomically */
1342 pthread_spin_lock(&server_opts.sid_lock);
1343 new_session->id = server_opts.new_session_id++;
1344 pthread_spin_unlock(&server_opts.sid_lock);
Michal Vaskofb89d772016-01-08 12:25:35 +01001345
Michal Vasko086311b2016-01-08 09:53:11 +01001346 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001347 msgtype = nc_handshake(new_session);
1348 if (msgtype != NC_MSG_HELLO) {
1349 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001350 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001351
Michal Vasko2f975cf2016-07-28 15:47:00 +02001352 new_session->session_start = new_session->last_rpc = time(NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001353 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001354 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001355
Michal Vasko71090fc2016-05-24 16:37:28 +02001356 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001357}