blob: de7bccb6c12771f4ae95f677d3f93830dffca424 [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
38nc_server_ssh_add_endpt_listen(const char *name, const char *address, uint16_t port)
39{
40 return nc_server_add_endpt_listen(name, address, port, NC_TI_LIBSSH);
41}
Michal Vasko086311b2016-01-08 09:53:11 +010042
Michal Vasko3031aae2016-01-27 16:07:18 +010043API int
Michal Vaskoda514772016-02-01 11:32:01 +010044nc_server_ssh_endpt_set_address(const char *endpt_name, const char *address)
45{
46 return nc_server_endpt_set_address_port(endpt_name, address, 0, NC_TI_LIBSSH);
47}
48
49API int
50nc_server_ssh_endpt_set_port(const char *endpt_name, uint16_t port)
51{
52 return nc_server_endpt_set_address_port(endpt_name, NULL, port, NC_TI_LIBSSH);
53}
54
55API int
Michal Vasko3031aae2016-01-27 16:07:18 +010056nc_server_ssh_del_endpt(const char *name)
57{
58 return nc_server_del_endpt(name, NC_TI_LIBSSH);
59}
Michal Vaskob05053d2016-01-22 16:12:06 +010060
61static int
Michal Vasko3031aae2016-01-27 16:07:18 +010062nc_server_ssh_set_hostkey(const char *privkey_path, struct nc_server_ssh_opts *opts)
Michal Vasko086311b2016-01-08 09:53:11 +010063{
Michal Vasko1a38c862016-01-15 15:50:07 +010064 if (!privkey_path) {
Michal Vasko45e53ae2016-04-07 11:46:03 +020065 ERRARG("privkey_path");
Michal Vasko086311b2016-01-08 09:53:11 +010066 return -1;
67 }
68
Michal Vaskob05053d2016-01-22 16:12:06 +010069 if (!opts->sshbind) {
70 opts->sshbind = ssh_bind_new();
71 if (!opts->sshbind) {
Michal Vaskod083db62016-01-19 10:31:29 +010072 ERR("Failed to create a new ssh_bind.");
Michal Vasko5fcc7142016-02-02 12:21:10 +010073 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +010074 }
75 }
76
Michal Vaskob05053d2016-01-22 16:12:06 +010077 if (ssh_bind_options_set(opts->sshbind, SSH_BIND_OPTIONS_HOSTKEY, privkey_path) != SSH_OK) {
Michal Vaskoc61c4492016-01-25 11:13:34 +010078 if (eaccess(privkey_path, R_OK)) {
79 ERR("Failed to set host key (%s).", strerror(errno));
80 } else {
81 ERR("Failed to set host key (%s).", ssh_get_error(opts->sshbind));
82 }
Michal Vasko5fcc7142016-02-02 12:21:10 +010083 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +010084 }
Michal Vaskod45e25a2016-01-08 15:48:44 +010085
Michal Vasko5fcc7142016-02-02 12:21:10 +010086 return 0;
Michal Vaskob05053d2016-01-22 16:12:06 +010087}
88
89API int
Michal Vasko3031aae2016-01-27 16:07:18 +010090nc_server_ssh_endpt_set_hostkey(const char *endpt_name, const char *privkey_path)
Michal Vaskob05053d2016-01-22 16:12:06 +010091{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010092 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +010093 struct nc_endpt *endpt;
94
Michal Vasko51e514d2016-02-02 15:51:52 +010095 /* LOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010096 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +010097 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +010098 return -1;
99 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100100 ret = nc_server_ssh_set_hostkey(privkey_path, endpt->ti_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100101 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100102 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100103
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100104 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100105}
106
107API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100108nc_server_ssh_ch_set_hostkey(const char *privkey_path)
Michal Vaskob05053d2016-01-22 16:12:06 +0100109{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100110 int ret;
111
112 /* OPTS LOCK */
113 pthread_mutex_lock(&ssh_ch_opts_lock);
114 ret = nc_server_ssh_set_hostkey(privkey_path, &ssh_ch_opts);
115 /* OPTS UNLOCK */
116 pthread_mutex_unlock(&ssh_ch_opts_lock);
117
118 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100119}
120
121static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100122nc_server_ssh_set_banner(const char *banner, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100123{
Michal Vaskob05053d2016-01-22 16:12:06 +0100124 if (!banner) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200125 ERRARG("banner");
Michal Vaskob05053d2016-01-22 16:12:06 +0100126 return -1;
127 }
128
Michal Vaskob05053d2016-01-22 16:12:06 +0100129 if (!opts->sshbind) {
130 opts->sshbind = ssh_bind_new();
131 if (!opts->sshbind) {
132 ERR("Failed to create a new ssh_bind.");
Michal Vasko5fcc7142016-02-02 12:21:10 +0100133 return -1;
Michal Vaskob05053d2016-01-22 16:12:06 +0100134 }
135 }
136
137 ssh_bind_options_set(opts->sshbind, SSH_BIND_OPTIONS_BANNER, banner);
138
Michal Vaskob05053d2016-01-22 16:12:06 +0100139 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100140}
141
142API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100143nc_server_ssh_endpt_set_banner(const char *endpt_name, const char *banner)
Michal Vasko086311b2016-01-08 09:53:11 +0100144{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100145 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100146 struct nc_endpt *endpt;
147
Michal Vasko51e514d2016-02-02 15:51:52 +0100148 /* LOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100149 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100150 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100151 return -1;
152 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100153 ret = nc_server_ssh_set_banner(banner, endpt->ti_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100154 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100155 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100156
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100157 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100158}
159
160API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100161nc_server_ssh_ch_set_banner(const char *banner)
Michal Vasko086311b2016-01-08 09:53:11 +0100162{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100163 int ret;
164
165 /* OPTS LOCK */
166 pthread_mutex_lock(&ssh_ch_opts_lock);
167 ret = nc_server_ssh_set_banner(banner, &ssh_ch_opts);
168 /* OPTS UNLOCK */
169 pthread_mutex_unlock(&ssh_ch_opts_lock);
170
171 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100172}
173
174static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100175nc_server_ssh_set_auth_methods(int auth_methods, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100176{
Michal Vasko086311b2016-01-08 09:53:11 +0100177 if (!(auth_methods & NC_SSH_AUTH_PUBLICKEY) && !(auth_methods & NC_SSH_AUTH_PASSWORD)
178 && !(auth_methods & NC_SSH_AUTH_INTERACTIVE)) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200179 ERRARG("auth_methods");
Michal Vasko086311b2016-01-08 09:53:11 +0100180 return -1;
181 }
182
Michal Vaskob05053d2016-01-22 16:12:06 +0100183 opts->auth_methods = auth_methods;
184 return 0;
185}
186
187API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100188nc_server_ssh_endpt_set_auth_methods(const char *endpt_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100189{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100190 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100191 struct nc_endpt *endpt;
192
Michal Vasko51e514d2016-02-02 15:51:52 +0100193 /* LOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100194 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100195 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100196 return -1;
197 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100198 ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->ti_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100199 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100200 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100201
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100202 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100203}
204
205API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100206nc_server_ssh_ch_set_auth_methods(int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100207{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100208 int ret;
209
210 /* OPTS LOCK */
211 pthread_mutex_lock(&ssh_ch_opts_lock);
212 ret = nc_server_ssh_set_auth_methods(auth_methods, &ssh_ch_opts);
213 /* OPTS UNLOCK */
214 pthread_mutex_unlock(&ssh_ch_opts_lock);
215
216 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100217}
218
219static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100220nc_server_ssh_set_auth_attempts(uint16_t auth_attempts, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100221{
Michal Vaskob05053d2016-01-22 16:12:06 +0100222 if (!auth_attempts) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200223 ERRARG("auth_attempts");
Michal Vaskob05053d2016-01-22 16:12:06 +0100224 return -1;
225 }
226
Michal Vaskob05053d2016-01-22 16:12:06 +0100227 opts->auth_attempts = auth_attempts;
Michal Vasko086311b2016-01-08 09:53:11 +0100228 return 0;
229}
230
231API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100232nc_server_ssh_endpt_set_auth_attempts(const char *endpt_name, uint16_t auth_attempts)
Michal Vasko086311b2016-01-08 09:53:11 +0100233{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100234 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100235 struct nc_endpt *endpt;
236
Michal Vasko51e514d2016-02-02 15:51:52 +0100237 /* LOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100238 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100239 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100240 return -1;
241 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100242 ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->ti_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100243 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100244 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100245
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100246 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100247}
248
249API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100250nc_server_ssh_set_ch_auth_attempts(uint16_t auth_attempts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100251{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100252 int ret;
253
254 /* OPTS LOCK */
255 pthread_mutex_lock(&ssh_ch_opts_lock);
256 ret = nc_server_ssh_set_auth_attempts(auth_attempts, &ssh_ch_opts);
257 /* OPTS UNLOCK */
258 pthread_mutex_unlock(&ssh_ch_opts_lock);
259
260 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100261}
262
263static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100264nc_server_ssh_set_auth_timeout(uint16_t auth_timeout, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100265{
Michal Vaskob05053d2016-01-22 16:12:06 +0100266 if (!auth_timeout) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200267 ERRARG("auth_timeout");
Michal Vasko086311b2016-01-08 09:53:11 +0100268 return -1;
269 }
270
Michal Vaskob05053d2016-01-22 16:12:06 +0100271 opts->auth_timeout = auth_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100272 return 0;
273}
274
275API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100276nc_server_ssh_endpt_set_auth_timeout(const char *endpt_name, uint16_t auth_timeout)
Michal Vasko086311b2016-01-08 09:53:11 +0100277{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100278 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100279 struct nc_endpt *endpt;
280
Michal Vasko51e514d2016-02-02 15:51:52 +0100281 /* LOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100282 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100283 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100284 return -1;
285 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100286 ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->ti_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100287 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100288 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100289
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100290 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100291}
292
293API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100294nc_server_ssh_ch_set_auth_timeout(uint16_t auth_timeout)
Michal Vaskob05053d2016-01-22 16:12:06 +0100295{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100296 int ret;
297
298 /* OPTS LOCK */
299 pthread_mutex_lock(&ssh_ch_opts_lock);
300 ret = nc_server_ssh_set_auth_timeout(auth_timeout, &ssh_ch_opts);
301 /* OPTS UNLOCK */
302 pthread_mutex_unlock(&ssh_ch_opts_lock);
303
304 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100305}
306
307static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100308nc_server_ssh_add_authkey(const char *pubkey_path, const char *username, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100309{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200310 if (!pubkey_path) {
311 ERRARG("pubkey_path");
312 return -1;
313 } else if (!username) {
314 ERRARG("username");
Michal Vasko086311b2016-01-08 09:53:11 +0100315 return -1;
316 }
317
Michal Vaskob05053d2016-01-22 16:12:06 +0100318 ++opts->authkey_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100319 opts->authkeys = nc_realloc(opts->authkeys, opts->authkey_count * sizeof *opts->authkeys);
320 if (!opts->authkeys) {
321 ERRMEM;
322 return -1;
323 }
Michal Vaskob05053d2016-01-22 16:12:06 +0100324 opts->authkeys[opts->authkey_count - 1].path = lydict_insert(server_opts.ctx, pubkey_path, 0);
325 opts->authkeys[opts->authkey_count - 1].username = lydict_insert(server_opts.ctx, username, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +0100326
Michal Vasko086311b2016-01-08 09:53:11 +0100327 return 0;
328}
329
330API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100331nc_server_ssh_endpt_add_authkey(const char *endpt_name, const char *pubkey_path, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100332{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100333 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100334 struct nc_endpt *endpt;
335
Michal Vasko51e514d2016-02-02 15:51:52 +0100336 /* LOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100337 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100338 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100339 return -1;
340 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100341 ret = nc_server_ssh_add_authkey(pubkey_path, username, endpt->ti_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100342 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100343 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100344
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100345 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100346}
347
348API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100349nc_server_ssh_ch_add_authkey(const char *pubkey_path, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100350{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100351 int ret;
352
353 /* OPTS LOCK */
354 pthread_mutex_lock(&ssh_ch_opts_lock);
355 ret = nc_server_ssh_add_authkey(pubkey_path, username, &ssh_ch_opts);
356 /* OPTS UNLOCK */
357 pthread_mutex_unlock(&ssh_ch_opts_lock);
358
359 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100360}
361
362static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100363nc_server_ssh_del_authkey(const char *pubkey_path, const char *username, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100364{
Michal Vasko086311b2016-01-08 09:53:11 +0100365 uint32_t i;
366 int ret = -1;
367
Michal Vasko1a38c862016-01-15 15:50:07 +0100368 if (!pubkey_path && !username) {
Michal Vaskob05053d2016-01-22 16:12:06 +0100369 for (i = 0; i < opts->authkey_count; ++i) {
370 lydict_remove(server_opts.ctx, opts->authkeys[i].path);
371 lydict_remove(server_opts.ctx, opts->authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100372
Michal Vasko086311b2016-01-08 09:53:11 +0100373 ret = 0;
374 }
Michal Vaskob05053d2016-01-22 16:12:06 +0100375 free(opts->authkeys);
376 opts->authkeys = NULL;
377 opts->authkey_count = 0;
Michal Vasko1a38c862016-01-15 15:50:07 +0100378 } else {
Michal Vaskob05053d2016-01-22 16:12:06 +0100379 for (i = 0; i < opts->authkey_count; ++i) {
380 if ((!pubkey_path || !strcmp(opts->authkeys[i].path, pubkey_path))
381 && (!username || !strcmp(opts->authkeys[i].username, username))) {
Michal Vaskob05053d2016-01-22 16:12:06 +0100382 lydict_remove(server_opts.ctx, opts->authkeys[i].path);
383 lydict_remove(server_opts.ctx, opts->authkeys[i].username);
Michal Vasko1a38c862016-01-15 15:50:07 +0100384
Michal Vaskob05053d2016-01-22 16:12:06 +0100385 --opts->authkey_count;
Michal Vaskoc0256492016-02-02 12:19:06 +0100386 if (i < opts->authkey_count) {
387 memcpy(&opts->authkeys[i], &opts->authkeys[opts->authkey_count], sizeof *opts->authkeys);
388 } else if (!opts->authkey_count) {
389 free(opts->authkeys);
390 opts->authkeys = NULL;
391 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100392
393 ret = 0;
394 }
395 }
Michal Vasko086311b2016-01-08 09:53:11 +0100396 }
397
398 return ret;
399}
400
Michal Vaskob05053d2016-01-22 16:12:06 +0100401API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100402nc_server_ssh_endpt_del_authkey(const char *endpt_name, const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100403{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100404 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100405 struct nc_endpt *endpt;
406
Michal Vasko51e514d2016-02-02 15:51:52 +0100407 /* LOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100408 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100409 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100410 return -1;
411 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100412 ret = nc_server_ssh_del_authkey(pubkey_path, username, endpt->ti_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100413 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100414 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100415
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100416 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100417}
418
419API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100420nc_server_ssh_ch_del_authkey(const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100421{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100422 int ret;
423
424 /* OPTS LOCK */
425 pthread_mutex_lock(&ssh_ch_opts_lock);
426 ret = nc_server_ssh_del_authkey(pubkey_path, username, &ssh_ch_opts);
427 /* OPTS UNLOCK */
428 pthread_mutex_unlock(&ssh_ch_opts_lock);
429
430 return ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100431}
432
433void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100434nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100435{
436 if (opts->sshbind) {
437 ssh_bind_free(opts->sshbind);
438 opts->sshbind = NULL;
439 }
440
441 nc_server_ssh_del_authkey(NULL, NULL, opts);
Michal Vaskob05053d2016-01-22 16:12:06 +0100442}
443
Michal Vasko086311b2016-01-08 09:53:11 +0100444API void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100445nc_server_ssh_ch_clear_opts(void)
Michal Vasko086311b2016-01-08 09:53:11 +0100446{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100447 /* OPTS LOCK */
448 pthread_mutex_lock(&ssh_ch_opts_lock);
449 nc_server_ssh_clear_opts(&ssh_ch_opts);
450 /* OPTS UNLOCK */
451 pthread_mutex_unlock(&ssh_ch_opts_lock);
Michal Vasko086311b2016-01-08 09:53:11 +0100452}
453
454static char *
455auth_password_get_pwd_hash(const char *username)
456{
457 struct passwd *pwd, pwd_buf;
458 struct spwd *spwd, spwd_buf;
459 char *pass_hash = NULL, buf[256];
460
461 getpwnam_r(username, &pwd_buf, buf, 256, &pwd);
462 if (!pwd) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100463 VRB("User \"%s\" not found locally.", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100464 return NULL;
465 }
466
467 if (!strcmp(pwd->pw_passwd, "x")) {
468 getspnam_r(username, &spwd_buf, buf, 256, &spwd);
469 if (!spwd) {
470 VRB("Failed to retrieve the shadow entry for \"%s\".", username);
471 return NULL;
472 }
473
474 pass_hash = spwd->sp_pwdp;
475 } else {
476 pass_hash = pwd->pw_passwd;
477 }
478
479 if (!pass_hash) {
Michal Vaskod083db62016-01-19 10:31:29 +0100480 ERR("No password could be retrieved for \"%s\".", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100481 return NULL;
482 }
483
484 /* check the hash structure for special meaning */
485 if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) {
486 VRB("User \"%s\" is not allowed to authenticate using a password.", username);
487 return NULL;
488 }
489 if (!strcmp(pass_hash, "*NP*")) {
490 VRB("Retrieving password for \"%s\" from a NIS+ server not supported.", username);
491 return NULL;
492 }
493
494 return strdup(pass_hash);
495}
496
497static int
498auth_password_compare_pwd(const char *pass_hash, const char *pass_clear)
499{
500 char *new_pass_hash;
501 struct crypt_data cdata;
502
503 if (!pass_hash[0]) {
504 if (!pass_clear[0]) {
505 WRN("User authentication successful with an empty password!");
506 return 0;
507 } else {
508 /* the user did now know he does not need any password,
509 * (which should not be used) so deny authentication */
510 return 1;
511 }
512 }
513
514 cdata.initialized = 0;
515 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
516 return strcmp(new_pass_hash, pass_hash);
517}
518
519static void
520nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
521{
522 char *pass_hash;
523
524 pass_hash = auth_password_get_pwd_hash(session->username);
525 if (pass_hash && !auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100526 VRB("User \"%s\" authenticated.", session->username);
Michal Vasko086311b2016-01-08 09:53:11 +0100527 ssh_message_auth_reply_success(msg, 0);
528 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
529 free(pass_hash);
530 return;
531 }
532
533 free(pass_hash);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100534 ++session->ssh_auth_attempts;
Michal Vasko296fee82016-05-04 08:57:31 +0200535 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100536 ssh_message_reply_default(msg);
537}
538
539static void
540nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
541{
542 char *pass_hash;
543
544 if (!ssh_message_auth_kbdint_is_response(msg)) {
545 const char *prompts[] = {"Password: "};
546 char echo[] = {0};
547
548 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
549 } else {
550 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {
551 ssh_message_reply_default(msg);
552 return;
553 }
554 pass_hash = auth_password_get_pwd_hash(session->username);
555 if (!pass_hash) {
556 ssh_message_reply_default(msg);
557 return;
558 }
559 if (!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0))) {
560 VRB("User \"%s\" authenticated.", session->username);
561 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
562 ssh_message_auth_reply_success(msg, 0);
563 } else {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100564 ++session->ssh_auth_attempts;
565 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100566 ssh_message_reply_default(msg);
567 }
Radek Krejcifb533742016-03-04 15:12:54 +0100568 free(pass_hash);
Michal Vasko086311b2016-01-08 09:53:11 +0100569 }
570}
571
572static const char *
Michal Vasko3031aae2016-01-27 16:07:18 +0100573auth_pubkey_compare_key(struct nc_server_ssh_opts *opts, ssh_key key)
Michal Vasko086311b2016-01-08 09:53:11 +0100574{
575 uint32_t i;
576 ssh_key pub_key;
Michal Vasko76e3a352016-01-18 09:07:00 +0100577 const char *username = NULL;
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200578 int ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100579
Michal Vasko3031aae2016-01-27 16:07:18 +0100580 for (i = 0; i < opts->authkey_count; ++i) {
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200581 ret = ssh_pki_import_pubkey_file(opts->authkeys[i].path, &pub_key);
582 if (ret == SSH_EOF) {
583 WRN("Failed to import the public key \"%s\" (File access problem).", opts->authkeys[i].path);
584 continue;
585 } else if (ret == SSH_ERROR) {
586 WRN("Failed to import the public key \"%s\" (SSH error).", opts->authkeys[i].path);
Michal Vasko086311b2016-01-08 09:53:11 +0100587 continue;
588 }
589
590 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
591 ssh_key_free(pub_key);
592 break;
593 }
594
595 ssh_key_free(pub_key);
596 }
597
Michal Vasko3031aae2016-01-27 16:07:18 +0100598 if (i < opts->authkey_count) {
599 username = opts->authkeys[i].username;
Michal Vasko086311b2016-01-08 09:53:11 +0100600 }
601
602 return username;
603}
604
605static void
606nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
607{
608 const char *username;
609 int signature_state;
610
611 signature_state = ssh_message_auth_publickey_state(msg);
612 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
613 VRB("User \"%s\" authenticated.", session->username);
614 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
615 ssh_message_auth_reply_success(msg, 0);
616 return;
617
618 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Michal Vasko2cc4c682016-03-01 09:16:48 +0100619 if ((username = auth_pubkey_compare_key(session->data, ssh_message_auth_pubkey(msg))) == NULL) {
Michal Vasko086311b2016-01-08 09:53:11 +0100620 VRB("User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
621
622 } else if (strcmp(session->username, username)) {
623 VRB("User \"%s\" is not the username identified with the presented public key.", session->username);
624
625 } else {
626 /* accepting only the use of a public key */
627 ssh_message_auth_reply_pk_ok_simple(msg);
628 return;
629 }
630 }
631
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100632 ++session->ssh_auth_attempts;
633 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100634 ssh_message_reply_default(msg);
635}
636
637static int
Michal Vasko96164bf2016-01-21 15:41:58 +0100638nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100639{
Michal Vasko96164bf2016-01-21 15:41:58 +0100640 ssh_channel chan;
641
642 /* first channel request */
643 if (!session->ti.libssh.channel) {
644 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100645 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100646 return -1;
647 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100648 chan = ssh_message_channel_request_open_reply_accept(msg);
649 if (!chan) {
650 ERR("Failed to create a new SSH channel.");
651 return -1;
652 }
653 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +0100654
Michal Vasko96164bf2016-01-21 15:41:58 +0100655 /* additional channel request */
656 } else {
657 chan = ssh_message_channel_request_open_reply_accept(msg);
658 if (!chan) {
659 ERR("Session %u: failed to create a new SSH channel.", session->id);
660 return -1;
661 }
662 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +0100663 }
664
Michal Vasko086311b2016-01-08 09:53:11 +0100665 return 0;
666}
667
668static int
669nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
670{
Michal Vasko96164bf2016-01-21 15:41:58 +0100671 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +0100672
Michal Vasko96164bf2016-01-21 15:41:58 +0100673 if (strcmp(subsystem, "netconf")) {
674 WRN("Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +0100675 return -1;
676 }
677
Michal Vasko96164bf2016-01-21 15:41:58 +0100678 if (session->ti.libssh.channel == channel) {
679 /* first channel requested */
680 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
681 ERRINT;
682 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100683 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100684 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
685 ERR("Session %u: subsystem \"netconf\" requested for the second time.", session->id);
686 return -1;
687 }
688
689 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +0100690 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +0100691 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
692 new_session = calloc(1, sizeof *new_session);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100693 if (!new_session) {
694 ERRMEM;
695 return -1;
696 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100697
698 /* insert the new session */
699 if (!session->ti.libssh.next) {
700 new_session->ti.libssh.next = session;
701 } else {
702 new_session->ti.libssh.next = session->ti.libssh.next;
703 }
704 session->ti.libssh.next = new_session;
705
706 new_session->status = NC_STATUS_STARTING;
707 new_session->side = NC_SERVER;
708 new_session->ti_type = NC_TI_LIBSSH;
709 new_session->ti_lock = session->ti_lock;
710 new_session->ti.libssh.channel = channel;
711 new_session->ti.libssh.session = session->ti.libssh.session;
712 new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
713 new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
714 new_session->port = session->port;
715 new_session->ctx = server_opts.ctx;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100716 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX
717 | (session->flags & NC_SESSION_CALLHOME ? NC_SESSION_CALLHOME : 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100718 }
719
720 return 0;
721}
722
Michal Vasko96164bf2016-01-21 15:41:58 +0100723int
Michal Vaskob48aa812016-01-18 14:13:09 +0100724nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +0100725{
726 const char *str_type, *str_subtype = NULL, *username;
727 int subtype, type;
728 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +0100729
730 type = ssh_message_type(msg);
731 subtype = ssh_message_subtype(msg);
732
733 switch (type) {
734 case SSH_REQUEST_AUTH:
735 str_type = "request-auth";
736 switch (subtype) {
737 case SSH_AUTH_METHOD_NONE:
738 str_subtype = "none";
739 break;
740 case SSH_AUTH_METHOD_PASSWORD:
741 str_subtype = "password";
742 break;
743 case SSH_AUTH_METHOD_PUBLICKEY:
744 str_subtype = "publickey";
745 break;
746 case SSH_AUTH_METHOD_HOSTBASED:
747 str_subtype = "hostbased";
748 break;
749 case SSH_AUTH_METHOD_INTERACTIVE:
750 str_subtype = "interactive";
751 break;
752 case SSH_AUTH_METHOD_GSSAPI_MIC:
753 str_subtype = "gssapi-mic";
754 break;
755 }
756 break;
757
758 case SSH_REQUEST_CHANNEL_OPEN:
759 str_type = "request-channel-open";
760 switch (subtype) {
761 case SSH_CHANNEL_SESSION:
762 str_subtype = "session";
763 break;
764 case SSH_CHANNEL_DIRECT_TCPIP:
765 str_subtype = "direct-tcpip";
766 break;
767 case SSH_CHANNEL_FORWARDED_TCPIP:
768 str_subtype = "forwarded-tcpip";
769 break;
770 case (int)SSH_CHANNEL_X11:
771 str_subtype = "channel-x11";
772 break;
773 case SSH_CHANNEL_UNKNOWN:
774 /* fallthrough */
775 default:
776 str_subtype = "unknown";
777 break;
778 }
779 break;
780
781 case SSH_REQUEST_CHANNEL:
782 str_type = "request-channel";
783 switch (subtype) {
784 case SSH_CHANNEL_REQUEST_PTY:
785 str_subtype = "pty";
786 break;
787 case SSH_CHANNEL_REQUEST_EXEC:
788 str_subtype = "exec";
789 break;
790 case SSH_CHANNEL_REQUEST_SHELL:
791 str_subtype = "shell";
792 break;
793 case SSH_CHANNEL_REQUEST_ENV:
794 str_subtype = "env";
795 break;
796 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
797 str_subtype = "subsystem";
798 break;
799 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
800 str_subtype = "window-change";
801 break;
802 case SSH_CHANNEL_REQUEST_X11:
803 str_subtype = "x11";
804 break;
805 case SSH_CHANNEL_REQUEST_UNKNOWN:
806 /* fallthrough */
807 default:
808 str_subtype = "unknown";
809 break;
810 }
811 break;
812
813 case SSH_REQUEST_SERVICE:
814 str_type = "request-service";
815 str_subtype = ssh_message_service_service(msg);
816 break;
817
818 case SSH_REQUEST_GLOBAL:
819 str_type = "request-global";
820 switch (subtype) {
821 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
822 str_subtype = "tcpip-forward";
823 break;
824 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
825 str_subtype = "cancel-tcpip-forward";
826 break;
827 case SSH_GLOBAL_REQUEST_UNKNOWN:
828 /* fallthrough */
829 default:
830 str_subtype = "unknown";
831 break;
832 }
833 break;
834
835 default:
836 str_type = "unknown";
837 str_subtype = "unknown";
838 break;
839 }
840
841 VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vaskoce319162016-02-03 15:33:08 +0100842 if ((session->status == NC_STATUS_CLOSING) || (session->status == NC_STATUS_INVALID)) {
843 /* "valid" situation if, for example, receiving some auth or channel request timeouted,
844 * but we got it now, during session free */
845 VRB("SSH message arrived on a %s session, the request will be denied.",
846 (session->status == NC_STATUS_CLOSING ? "closing" : "invalid"));
847 ssh_message_reply_default(msg);
848 return 0;
849 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100850 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +0100851
852 /*
853 * process known messages
854 */
855 if (type == SSH_REQUEST_AUTH) {
856 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
857 ERR("User \"%s\" authenticated, but requested another authentication.", session->username);
858 ssh_message_reply_default(msg);
859 return 0;
860 }
861
Michal Vasko2cc4c682016-03-01 09:16:48 +0100862 if (session->ssh_auth_attempts >= ((struct nc_server_ssh_opts *)session->data)->auth_attempts) {
Michal Vasko086311b2016-01-08 09:53:11 +0100863 /* too many failed attempts */
864 ssh_message_reply_default(msg);
865 return 0;
866 }
867
868 /* save the username, do not let the client change it */
869 username = ssh_message_auth_user(msg);
870 if (!session->username) {
871 if (!username) {
872 ERR("Denying an auth request without a username.");
873 return 1;
874 }
875
Michal Vasko05ba9df2016-01-13 14:40:27 +0100876 session->username = lydict_insert(server_opts.ctx, username, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100877 } else if (username) {
878 if (strcmp(username, session->username)) {
879 ERR("User \"%s\" changed its username to \"%s\".", session->username, username);
880 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +0100881 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +0100882 return 1;
883 }
884 }
885
886 if (subtype == SSH_AUTH_METHOD_NONE) {
887 /* libssh will return the supported auth methods */
888 return 1;
889 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
890 nc_sshcb_auth_password(session, msg);
891 return 0;
892 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
893 nc_sshcb_auth_pubkey(session, msg);
894 return 0;
895 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
896 nc_sshcb_auth_kbdint(session, msg);
897 return 0;
898 }
899 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +0100900 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100901 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100902 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100903 }
Michal Vasko086311b2016-01-08 09:53:11 +0100904 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +0100905
Michal Vasko0df67562016-01-21 15:50:11 +0100906 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100907 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
908 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +0100909 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +0100910 } else {
911 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100912 }
913 return 0;
914 }
915 }
916
917 /* we did not process it */
918 return 1;
919}
920
Michal Vasko1a38c862016-01-15 15:50:07 +0100921/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +0100922static int
923nc_open_netconf_channel(struct nc_session *session, int timeout)
924{
Michal Vasko62be1ce2016-03-03 13:24:52 +0100925 int elapsed_usec = 0, ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100926
927 /* message callback is executed twice to give chance for the channel to be
928 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100929 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +0100930 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100931 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +0100932 return -1;
933 }
934
Michal Vasko62be1ce2016-03-03 13:24:52 +0100935 ret = nc_timedlock(session->ti_lock, timeout);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100936 if (ret != 1) {
937 return ret;
938 }
939
940 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
941 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100942 ERR("Failed to receive SSH messages on a session (%s).",
943 ssh_get_error(session->ti.libssh.session));
Michal Vasko11d142a2016-01-19 15:58:24 +0100944 pthread_mutex_unlock(session->ti_lock);
Michal Vasko086311b2016-01-08 09:53:11 +0100945 return -1;
946 }
947
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100948 if (!session->ti.libssh.channel) {
949 /* we did not receive channel-open, timeout */
950 pthread_mutex_unlock(session->ti_lock);
951 return 0;
952 }
953
954 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
955 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100956 ERR("Failed to receive SSH messages on a session (%s).",
957 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100958 pthread_mutex_unlock(session->ti_lock);
959 return -1;
960 }
961 pthread_mutex_unlock(session->ti_lock);
962
963 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
964 /* we did not receive subsystem-request, timeout */
965 return 0;
966 }
967
968 return 1;
969 }
970
971 while (1) {
972 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100973 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100974 return -1;
975 }
976
Michal Vasko62be1ce2016-03-03 13:24:52 +0100977 ret = nc_timedlock(session->ti_lock, timeout);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100978 if (ret != 1) {
979 return ret;
980 }
981
982 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
983 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100984 ERR("Failed to receive SSH messages on a session (%s).",
985 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100986 pthread_mutex_unlock(session->ti_lock);
987 return -1;
988 }
989
990 pthread_mutex_unlock(session->ti_lock);
991
Michal Vasko086311b2016-01-08 09:53:11 +0100992 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100993 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +0100994 }
995
Michal Vasko105bf272016-02-03 15:34:35 +0100996 if ((timeout != -1) && (elapsed_usec / 1000 >= timeout)) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100997 /* timeout */
Michal Vasko842522c2016-03-01 09:20:13 +0100998 ERR("Failed to start \"netconf\" SSH subsystem for too long, disconnecting.");
Michal Vasko086311b2016-01-08 09:53:11 +0100999 break;
1000 }
1001
Michal Vasko086311b2016-01-08 09:53:11 +01001002 usleep(NC_TIMEOUT_STEP);
Michal Vasko105bf272016-02-03 15:34:35 +01001003 elapsed_usec += NC_TIMEOUT_STEP;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001004 }
Michal Vasko086311b2016-01-08 09:53:11 +01001005
Michal Vasko1a38c862016-01-15 15:50:07 +01001006 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001007}
1008
Michal Vasko96164bf2016-01-21 15:41:58 +01001009int
Michal Vasko62be1ce2016-03-03 13:24:52 +01001010nc_ssh_pollin(struct nc_session *session, int timeout)
Michal Vasko96164bf2016-01-21 15:41:58 +01001011{
Michal Vasko62be1ce2016-03-03 13:24:52 +01001012 int ret;
Michal Vasko96164bf2016-01-21 15:41:58 +01001013 struct nc_session *new;
1014
Michal Vasko62be1ce2016-03-03 13:24:52 +01001015 ret = nc_timedlock(session->ti_lock, timeout);
Michal Vasko96164bf2016-01-21 15:41:58 +01001016
Michal Vasko71090fc2016-05-24 16:37:28 +02001017 if (ret < 0) {
1018 return NC_PSPOLL_ERROR;
1019 } else if (!ret) {
1020 return NC_PSPOLL_TIMEOUT;
Michal Vasko96164bf2016-01-21 15:41:58 +01001021 }
1022
1023 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1024 pthread_mutex_unlock(session->ti_lock);
1025
1026 if (ret != SSH_OK) {
1027 ERR("Session %u: failed to receive SSH messages (%s).", session->id,
1028 ssh_get_error(session->ti.libssh.session));
1029 session->status = NC_STATUS_INVALID;
1030 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko71090fc2016-05-24 16:37:28 +02001031 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001032 }
1033
1034 /* new SSH message */
1035 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1036 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1037 if (session->ti.libssh.next) {
1038 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1039 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1040 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1041 /* new NETCONF SSH channel */
Michal Vasko71090fc2016-05-24 16:37:28 +02001042 return NC_PSPOLL_SSH_CHANNEL;
Michal Vasko96164bf2016-01-21 15:41:58 +01001043 }
1044 }
1045 }
1046
1047 /* just some SSH message */
Michal Vasko71090fc2016-05-24 16:37:28 +02001048 return NC_PSPOLL_SSH_MSG;
Michal Vasko96164bf2016-01-21 15:41:58 +01001049 }
1050
1051 /* no new SSH message, maybe NETCONF data? */
1052 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
1053 /* not this one */
1054 if (!ret) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001055 return NC_PSPOLL_PENDING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001056 } else if (ret == SSH_ERROR) {
1057 ERR("Session %u: SSH channel error (%s).", session->id,
1058 ssh_get_error(session->ti.libssh.session));
1059 session->status = NC_STATUS_INVALID;
1060 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko71090fc2016-05-24 16:37:28 +02001061 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001062 } else if (ret == SSH_EOF) {
1063 ERR("Session %u: communication channel unexpectedly closed (libssh).",
1064 session->id);
1065 session->status = NC_STATUS_INVALID;
1066 session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko71090fc2016-05-24 16:37:28 +02001067 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001068 }
1069
Michal Vasko71090fc2016-05-24 16:37:28 +02001070 return NC_PSPOLL_RPC;
Michal Vasko96164bf2016-01-21 15:41:58 +01001071}
1072
Michal Vasko71090fc2016-05-24 16:37:28 +02001073API NC_MSG_TYPE
Michal Vasko8f5270d2016-02-29 16:22:25 +01001074nc_connect_callhome_ssh(const char *host, uint16_t port, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001075{
Michal Vasko8f5270d2016-02-29 16:22:25 +01001076 return nc_connect_callhome(host, port, NC_TI_LIBSSH, session);
Michal Vasko3031aae2016-01-27 16:07:18 +01001077}
1078
1079int
Michal Vasko0190bc32016-03-02 15:47:49 +01001080nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko3031aae2016-01-27 16:07:18 +01001081{
1082 struct nc_server_ssh_opts *opts;
Michal Vasko72387da2016-02-02 15:52:41 +01001083 int libssh_auth_methods = 0, elapsed_usec = 0, ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001084
Michal Vasko2cc4c682016-03-01 09:16:48 +01001085 opts = session->data;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001086
Michal Vasko086311b2016-01-08 09:53:11 +01001087 /* other transport-specific data */
1088 session->ti_type = NC_TI_LIBSSH;
1089 session->ti.libssh.session = ssh_new();
1090 if (!session->ti.libssh.session) {
Michal Vaskod083db62016-01-19 10:31:29 +01001091 ERR("Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001092 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001093 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001094 }
1095
Michal Vaskoc61c4492016-01-25 11:13:34 +01001096 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001097 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1098 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001099 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001100 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1101 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001102 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001103 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1104 }
1105 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1106
1107 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001108 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001109 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001110
Michal Vaskoc61c4492016-01-25 11:13:34 +01001111 if (ssh_bind_accept_fd(opts->sshbind, session->ti.libssh.session, sock) == SSH_ERROR) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001112 ERR("SSH failed to accept a new connection (%s).", ssh_get_error(opts->sshbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001113 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001114 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001115 }
1116
Michal Vasko0190bc32016-03-02 15:47:49 +01001117 ssh_set_blocking(session->ti.libssh.session, 0);
1118
1119 while ((ret = ssh_handle_key_exchange(session->ti.libssh.session)) == SSH_AGAIN) {
Michal Vaskoe65b4492016-03-03 11:57:51 +01001120 /* this tends to take longer */
1121 usleep(NC_TIMEOUT_STEP * 20);
1122 elapsed_usec += NC_TIMEOUT_STEP * 20;
Michal Vasko0190bc32016-03-02 15:47:49 +01001123 if ((timeout > -1) && (elapsed_usec / 1000 >= timeout)) {
1124 break;
1125 }
1126 }
1127 if (ret == SSH_AGAIN) {
1128 ERR("SSH key exchange timeout.");
1129 return 0;
1130 } else if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001131 ERR("SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001132 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001133 }
1134
1135 /* authenticate */
Michal Vasko0190bc32016-03-02 15:47:49 +01001136 elapsed_usec = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001137 do {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001138 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001139 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001140 return -1;
1141 }
1142
Michal Vasko086311b2016-01-08 09:53:11 +01001143 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001144 ERR("Failed to receive SSH messages on a session (%s).",
1145 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001146 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001147 }
1148
1149 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1150 break;
1151 }
1152
1153 usleep(NC_TIMEOUT_STEP);
Michal Vasko72387da2016-02-02 15:52:41 +01001154 elapsed_usec += NC_TIMEOUT_STEP;
Michal Vaskoab639942016-02-29 16:23:47 +01001155 } while (!opts->auth_timeout || (elapsed_usec / 1000000 < opts->auth_timeout));
Michal Vasko086311b2016-01-08 09:53:11 +01001156
1157 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1158 /* timeout */
Michal Vasko842522c2016-03-01 09:20:13 +01001159 ERR("Client failed to authenticate for too long, disconnecting.");
Michal Vasko1a38c862016-01-15 15:50:07 +01001160 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001161 }
1162
Michal Vasko086311b2016-01-08 09:53:11 +01001163 /* open channel */
Michal Vaskoab639942016-02-29 16:23:47 +01001164 ret = nc_open_netconf_channel(session, opts->auth_timeout ? (opts->auth_timeout * 1000 - elapsed_usec / 1000) : -1);
Michal Vasko1a38c862016-01-15 15:50:07 +01001165 if (ret < 1) {
1166 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001167 }
1168
Michal Vasko96164bf2016-01-21 15:41:58 +01001169 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1170
Michal Vasko1a38c862016-01-15 15:50:07 +01001171 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001172}
1173
Michal Vasko71090fc2016-05-24 16:37:28 +02001174API NC_MSG_TYPE
1175nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session **session)
1176{
1177 NC_MSG_TYPE msgtype;
1178 struct nc_session *new_session = NULL;
1179
1180 if (!orig_session) {
1181 ERRARG("orig_session");
1182 return NC_MSG_ERROR;
1183 } else if (!session) {
1184 ERRARG("session");
1185 return NC_MSG_ERROR;
1186 }
1187
1188 if ((orig_session->status == NC_STATUS_RUNNING) && (orig_session->ti_type == NC_TI_LIBSSH)
1189 && orig_session->ti.libssh.next) {
1190 for (new_session = orig_session->ti.libssh.next;
1191 new_session != orig_session;
1192 new_session = new_session->ti.libssh.next) {
1193 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1194 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1195 /* we found our session */
1196 break;
1197 }
1198 }
1199 if (new_session == orig_session) {
1200 new_session = NULL;
1201 }
1202 }
1203
1204 if (!new_session) {
1205 ERR("Session does not have a NETCONF SSH channel ready.");
1206 return NC_MSG_ERROR;
1207 }
1208
1209 /* assign new SID atomically */
1210 pthread_spin_lock(&server_opts.sid_lock);
1211 new_session->id = server_opts.new_session_id++;
1212 pthread_spin_unlock(&server_opts.sid_lock);
1213
1214 /* NETCONF handshake */
1215 msgtype = nc_handshake(new_session);
1216 if (msgtype != NC_MSG_HELLO) {
1217 return msgtype;
1218 }
1219
1220 new_session->session_start = time(NULL);
1221 new_session->status = NC_STATUS_RUNNING;
1222 *session = new_session;
1223
1224 return msgtype;
1225}
1226
1227API NC_MSG_TYPE
Michal Vasko96164bf2016-01-21 15:41:58 +01001228nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001229{
Michal Vasko71090fc2016-05-24 16:37:28 +02001230 NC_MSG_TYPE msgtype;
Michal Vasko96164bf2016-01-21 15:41:58 +01001231 struct nc_session *new_session = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001232 uint16_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001233
Michal Vasko45e53ae2016-04-07 11:46:03 +02001234 if (!ps) {
1235 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001236 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001237 } else if (!session) {
1238 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001239 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01001240 }
1241
Michal Vasko48a63ed2016-03-01 09:48:21 +01001242 /* LOCK */
Michal Vaskof04a52a2016-04-07 10:52:10 +02001243 if (nc_ps_lock(ps)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001244 return NC_MSG_ERROR;
Michal Vaskof04a52a2016-04-07 10:52:10 +02001245 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001246
Michal Vasko96164bf2016-01-21 15:41:58 +01001247 for (i = 0; i < ps->session_count; ++i) {
1248 if ((ps->sessions[i]->status == NC_STATUS_RUNNING) && (ps->sessions[i]->ti_type == NC_TI_LIBSSH)
1249 && ps->sessions[i]->ti.libssh.next) {
1250 /* an SSH session with more channels */
1251 for (new_session = ps->sessions[i]->ti.libssh.next;
1252 new_session != ps->sessions[i];
1253 new_session = new_session->ti.libssh.next) {
1254 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1255 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1256 /* we found our session */
1257 break;
1258 }
1259 }
1260 if (new_session != ps->sessions[i]) {
1261 break;
1262 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001263
Michal Vasko96164bf2016-01-21 15:41:58 +01001264 new_session = NULL;
1265 }
1266 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001267
Michal Vasko48a63ed2016-03-01 09:48:21 +01001268 /* UNLOCK */
Michal Vaskof04a52a2016-04-07 10:52:10 +02001269 nc_ps_unlock(ps);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001270
Michal Vasko96164bf2016-01-21 15:41:58 +01001271 if (!new_session) {
1272 ERR("No session with a NETCONF SSH channel ready was found.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001273 return NC_MSG_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001274 }
1275
1276 /* assign new SID atomically */
1277 pthread_spin_lock(&server_opts.sid_lock);
1278 new_session->id = server_opts.new_session_id++;
1279 pthread_spin_unlock(&server_opts.sid_lock);
Michal Vaskofb89d772016-01-08 12:25:35 +01001280
Michal Vasko086311b2016-01-08 09:53:11 +01001281 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001282 msgtype = nc_handshake(new_session);
1283 if (msgtype != NC_MSG_HELLO) {
1284 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001285 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001286
Michal Vaskof8352352016-05-24 09:11:36 +02001287 new_session->session_start = time(NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001288 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001289 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001290
Michal Vasko71090fc2016-05-24 16:37:28 +02001291 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001292}