blob: 4f2770131e3ba8ddb3e805174e9dc93d7b3bc76d [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
11 *
12 * 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 Vasko086311b2016-01-08 09:53:11 +010065 ERRARG;
66 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) {
125 ERRARG;
126 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)) {
179 ERRARG;
180 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) {
223 ERRARG;
224 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 Vasko086311b2016-01-08 09:53:11 +0100267 ERRARG;
268 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 Vaskob05053d2016-01-22 16:12:06 +0100310 if (!pubkey_path || !username) {
Michal Vasko086311b2016-01-08 09:53:11 +0100311 ERRARG;
312 return -1;
313 }
314
Michal Vaskob05053d2016-01-22 16:12:06 +0100315 ++opts->authkey_count;
316 opts->authkeys = realloc(opts->authkeys, opts->authkey_count * sizeof *opts->authkeys);
Michal Vaskob05053d2016-01-22 16:12:06 +0100317 opts->authkeys[opts->authkey_count - 1].path = lydict_insert(server_opts.ctx, pubkey_path, 0);
318 opts->authkeys[opts->authkey_count - 1].username = lydict_insert(server_opts.ctx, username, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +0100319
Michal Vasko086311b2016-01-08 09:53:11 +0100320 return 0;
321}
322
323API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100324nc_server_ssh_endpt_add_authkey(const char *endpt_name, const char *pubkey_path, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100325{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100326 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100327 struct nc_endpt *endpt;
328
Michal Vasko51e514d2016-02-02 15:51:52 +0100329 /* LOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100330 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100331 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100332 return -1;
333 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100334 ret = nc_server_ssh_add_authkey(pubkey_path, username, endpt->ti_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100335 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100336 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100337
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100338 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100339}
340
341API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100342nc_server_ssh_ch_add_authkey(const char *pubkey_path, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100343{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100344 int ret;
345
346 /* OPTS LOCK */
347 pthread_mutex_lock(&ssh_ch_opts_lock);
348 ret = nc_server_ssh_add_authkey(pubkey_path, username, &ssh_ch_opts);
349 /* OPTS UNLOCK */
350 pthread_mutex_unlock(&ssh_ch_opts_lock);
351
352 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100353}
354
355static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100356nc_server_ssh_del_authkey(const char *pubkey_path, const char *username, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100357{
Michal Vasko086311b2016-01-08 09:53:11 +0100358 uint32_t i;
359 int ret = -1;
360
Michal Vasko1a38c862016-01-15 15:50:07 +0100361 if (!pubkey_path && !username) {
Michal Vaskob05053d2016-01-22 16:12:06 +0100362 for (i = 0; i < opts->authkey_count; ++i) {
363 lydict_remove(server_opts.ctx, opts->authkeys[i].path);
364 lydict_remove(server_opts.ctx, opts->authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100365
Michal Vasko086311b2016-01-08 09:53:11 +0100366 ret = 0;
367 }
Michal Vaskob05053d2016-01-22 16:12:06 +0100368 free(opts->authkeys);
369 opts->authkeys = NULL;
370 opts->authkey_count = 0;
Michal Vasko1a38c862016-01-15 15:50:07 +0100371 } else {
Michal Vaskob05053d2016-01-22 16:12:06 +0100372 for (i = 0; i < opts->authkey_count; ++i) {
373 if ((!pubkey_path || !strcmp(opts->authkeys[i].path, pubkey_path))
374 && (!username || !strcmp(opts->authkeys[i].username, username))) {
Michal Vaskob05053d2016-01-22 16:12:06 +0100375 lydict_remove(server_opts.ctx, opts->authkeys[i].path);
376 lydict_remove(server_opts.ctx, opts->authkeys[i].username);
Michal Vasko1a38c862016-01-15 15:50:07 +0100377
Michal Vaskob05053d2016-01-22 16:12:06 +0100378 --opts->authkey_count;
Michal Vaskoc0256492016-02-02 12:19:06 +0100379 if (i < opts->authkey_count) {
380 memcpy(&opts->authkeys[i], &opts->authkeys[opts->authkey_count], sizeof *opts->authkeys);
381 } else if (!opts->authkey_count) {
382 free(opts->authkeys);
383 opts->authkeys = NULL;
384 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100385
386 ret = 0;
387 }
388 }
Michal Vasko086311b2016-01-08 09:53:11 +0100389 }
390
391 return ret;
392}
393
Michal Vaskob05053d2016-01-22 16:12:06 +0100394API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100395nc_server_ssh_endpt_del_authkey(const char *endpt_name, const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100396{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100397 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100398 struct nc_endpt *endpt;
399
Michal Vasko51e514d2016-02-02 15:51:52 +0100400 /* LOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100401 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100402 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100403 return -1;
404 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100405 ret = nc_server_ssh_del_authkey(pubkey_path, username, endpt->ti_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100406 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100407 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100408
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100409 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100410}
411
412API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100413nc_server_ssh_ch_del_authkey(const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100414{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100415 int ret;
416
417 /* OPTS LOCK */
418 pthread_mutex_lock(&ssh_ch_opts_lock);
419 ret = nc_server_ssh_del_authkey(pubkey_path, username, &ssh_ch_opts);
420 /* OPTS UNLOCK */
421 pthread_mutex_unlock(&ssh_ch_opts_lock);
422
423 return ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100424}
425
426void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100427nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100428{
429 if (opts->sshbind) {
430 ssh_bind_free(opts->sshbind);
431 opts->sshbind = NULL;
432 }
433
434 nc_server_ssh_del_authkey(NULL, NULL, opts);
Michal Vaskob05053d2016-01-22 16:12:06 +0100435}
436
Michal Vasko086311b2016-01-08 09:53:11 +0100437API void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100438nc_server_ssh_ch_clear_opts(void)
Michal Vasko086311b2016-01-08 09:53:11 +0100439{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100440 /* OPTS LOCK */
441 pthread_mutex_lock(&ssh_ch_opts_lock);
442 nc_server_ssh_clear_opts(&ssh_ch_opts);
443 /* OPTS UNLOCK */
444 pthread_mutex_unlock(&ssh_ch_opts_lock);
Michal Vasko086311b2016-01-08 09:53:11 +0100445}
446
447static char *
448auth_password_get_pwd_hash(const char *username)
449{
450 struct passwd *pwd, pwd_buf;
451 struct spwd *spwd, spwd_buf;
452 char *pass_hash = NULL, buf[256];
453
454 getpwnam_r(username, &pwd_buf, buf, 256, &pwd);
455 if (!pwd) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100456 VRB("User \"%s\" not found locally.", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100457 return NULL;
458 }
459
460 if (!strcmp(pwd->pw_passwd, "x")) {
461 getspnam_r(username, &spwd_buf, buf, 256, &spwd);
462 if (!spwd) {
463 VRB("Failed to retrieve the shadow entry for \"%s\".", username);
464 return NULL;
465 }
466
467 pass_hash = spwd->sp_pwdp;
468 } else {
469 pass_hash = pwd->pw_passwd;
470 }
471
472 if (!pass_hash) {
Michal Vaskod083db62016-01-19 10:31:29 +0100473 ERR("No password could be retrieved for \"%s\".", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100474 return NULL;
475 }
476
477 /* check the hash structure for special meaning */
478 if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) {
479 VRB("User \"%s\" is not allowed to authenticate using a password.", username);
480 return NULL;
481 }
482 if (!strcmp(pass_hash, "*NP*")) {
483 VRB("Retrieving password for \"%s\" from a NIS+ server not supported.", username);
484 return NULL;
485 }
486
487 return strdup(pass_hash);
488}
489
490static int
491auth_password_compare_pwd(const char *pass_hash, const char *pass_clear)
492{
493 char *new_pass_hash;
494 struct crypt_data cdata;
495
496 if (!pass_hash[0]) {
497 if (!pass_clear[0]) {
498 WRN("User authentication successful with an empty password!");
499 return 0;
500 } else {
501 /* the user did now know he does not need any password,
502 * (which should not be used) so deny authentication */
503 return 1;
504 }
505 }
506
507 cdata.initialized = 0;
508 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
509 return strcmp(new_pass_hash, pass_hash);
510}
511
512static void
513nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
514{
515 char *pass_hash;
516
517 pass_hash = auth_password_get_pwd_hash(session->username);
518 if (pass_hash && !auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100519 VRB("User \"%s\" authenticated.", session->username);
Michal Vasko086311b2016-01-08 09:53:11 +0100520 ssh_message_auth_reply_success(msg, 0);
521 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
522 free(pass_hash);
523 return;
524 }
525
526 free(pass_hash);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100527 ++session->ssh_auth_attempts;
Michal Vaskod083db62016-01-19 10:31:29 +0100528 VRB("Failed user \"'%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100529 ssh_message_reply_default(msg);
530}
531
532static void
533nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
534{
535 char *pass_hash;
536
537 if (!ssh_message_auth_kbdint_is_response(msg)) {
538 const char *prompts[] = {"Password: "};
539 char echo[] = {0};
540
541 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
542 } else {
543 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {
544 ssh_message_reply_default(msg);
545 return;
546 }
547 pass_hash = auth_password_get_pwd_hash(session->username);
548 if (!pass_hash) {
549 ssh_message_reply_default(msg);
550 return;
551 }
552 if (!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0))) {
553 VRB("User \"%s\" authenticated.", session->username);
554 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
555 ssh_message_auth_reply_success(msg, 0);
556 } else {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100557 ++session->ssh_auth_attempts;
558 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100559 ssh_message_reply_default(msg);
560 }
561 }
562}
563
564static const char *
Michal Vasko3031aae2016-01-27 16:07:18 +0100565auth_pubkey_compare_key(struct nc_server_ssh_opts *opts, ssh_key key)
Michal Vasko086311b2016-01-08 09:53:11 +0100566{
567 uint32_t i;
568 ssh_key pub_key;
Michal Vasko76e3a352016-01-18 09:07:00 +0100569 const char *username = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100570
Michal Vasko3031aae2016-01-27 16:07:18 +0100571 for (i = 0; i < opts->authkey_count; ++i) {
572 if (ssh_pki_import_pubkey_file(opts->authkeys[i].path, &pub_key) != SSH_OK) {
573 if (eaccess(opts->authkeys[i].path, R_OK)) {
574 WRN("Failed to import the public key \"%s\" (%s).", opts->authkeys[i].path, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100575 } else {
Michal Vaskoe5612cf2016-02-02 15:52:16 +0100576 WRN("Failed to import the public key \"%s\" (%s).", opts->authkeys[i].path, ssh_get_error(pub_key));
Michal Vasko086311b2016-01-08 09:53:11 +0100577 }
578 continue;
579 }
580
581 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
582 ssh_key_free(pub_key);
583 break;
584 }
585
586 ssh_key_free(pub_key);
587 }
588
Michal Vasko3031aae2016-01-27 16:07:18 +0100589 if (i < opts->authkey_count) {
590 username = opts->authkeys[i].username;
Michal Vasko086311b2016-01-08 09:53:11 +0100591 }
592
593 return username;
594}
595
596static void
597nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
598{
599 const char *username;
600 int signature_state;
601
602 signature_state = ssh_message_auth_publickey_state(msg);
603 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
604 VRB("User \"%s\" authenticated.", session->username);
605 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
606 ssh_message_auth_reply_success(msg, 0);
607 return;
608
609 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100610 if ((username = auth_pubkey_compare_key(session->ti_opts, ssh_message_auth_pubkey(msg))) == NULL) {
Michal Vasko086311b2016-01-08 09:53:11 +0100611 VRB("User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
612
613 } else if (strcmp(session->username, username)) {
614 VRB("User \"%s\" is not the username identified with the presented public key.", session->username);
615
616 } else {
617 /* accepting only the use of a public key */
618 ssh_message_auth_reply_pk_ok_simple(msg);
619 return;
620 }
621 }
622
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100623 ++session->ssh_auth_attempts;
624 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100625 ssh_message_reply_default(msg);
626}
627
628static int
Michal Vasko96164bf2016-01-21 15:41:58 +0100629nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100630{
Michal Vasko96164bf2016-01-21 15:41:58 +0100631 ssh_channel chan;
632
633 /* first channel request */
634 if (!session->ti.libssh.channel) {
635 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100636 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100637 return -1;
638 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100639 chan = ssh_message_channel_request_open_reply_accept(msg);
640 if (!chan) {
641 ERR("Failed to create a new SSH channel.");
642 return -1;
643 }
644 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +0100645
Michal Vasko96164bf2016-01-21 15:41:58 +0100646 /* additional channel request */
647 } else {
648 chan = ssh_message_channel_request_open_reply_accept(msg);
649 if (!chan) {
650 ERR("Session %u: failed to create a new SSH channel.", session->id);
651 return -1;
652 }
653 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +0100654 }
655
Michal Vasko086311b2016-01-08 09:53:11 +0100656 return 0;
657}
658
659static int
660nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
661{
Michal Vasko96164bf2016-01-21 15:41:58 +0100662 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +0100663
Michal Vasko96164bf2016-01-21 15:41:58 +0100664 if (strcmp(subsystem, "netconf")) {
665 WRN("Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +0100666 return -1;
667 }
668
Michal Vasko96164bf2016-01-21 15:41:58 +0100669 if (session->ti.libssh.channel == channel) {
670 /* first channel requested */
671 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
672 ERRINT;
673 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100674 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100675 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
676 ERR("Session %u: subsystem \"netconf\" requested for the second time.", session->id);
677 return -1;
678 }
679
680 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +0100681 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +0100682 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
683 new_session = calloc(1, sizeof *new_session);
684
685 /* insert the new session */
686 if (!session->ti.libssh.next) {
687 new_session->ti.libssh.next = session;
688 } else {
689 new_session->ti.libssh.next = session->ti.libssh.next;
690 }
691 session->ti.libssh.next = new_session;
692
693 new_session->status = NC_STATUS_STARTING;
694 new_session->side = NC_SERVER;
695 new_session->ti_type = NC_TI_LIBSSH;
696 new_session->ti_lock = session->ti_lock;
697 new_session->ti.libssh.channel = channel;
698 new_session->ti.libssh.session = session->ti.libssh.session;
699 new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
700 new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
701 new_session->port = session->port;
702 new_session->ctx = server_opts.ctx;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100703 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX
704 | (session->flags & NC_SESSION_CALLHOME ? NC_SESSION_CALLHOME : 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100705 }
706
707 return 0;
708}
709
Michal Vasko96164bf2016-01-21 15:41:58 +0100710int
Michal Vaskob48aa812016-01-18 14:13:09 +0100711nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +0100712{
713 const char *str_type, *str_subtype = NULL, *username;
714 int subtype, type;
715 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +0100716
717 type = ssh_message_type(msg);
718 subtype = ssh_message_subtype(msg);
719
720 switch (type) {
721 case SSH_REQUEST_AUTH:
722 str_type = "request-auth";
723 switch (subtype) {
724 case SSH_AUTH_METHOD_NONE:
725 str_subtype = "none";
726 break;
727 case SSH_AUTH_METHOD_PASSWORD:
728 str_subtype = "password";
729 break;
730 case SSH_AUTH_METHOD_PUBLICKEY:
731 str_subtype = "publickey";
732 break;
733 case SSH_AUTH_METHOD_HOSTBASED:
734 str_subtype = "hostbased";
735 break;
736 case SSH_AUTH_METHOD_INTERACTIVE:
737 str_subtype = "interactive";
738 break;
739 case SSH_AUTH_METHOD_GSSAPI_MIC:
740 str_subtype = "gssapi-mic";
741 break;
742 }
743 break;
744
745 case SSH_REQUEST_CHANNEL_OPEN:
746 str_type = "request-channel-open";
747 switch (subtype) {
748 case SSH_CHANNEL_SESSION:
749 str_subtype = "session";
750 break;
751 case SSH_CHANNEL_DIRECT_TCPIP:
752 str_subtype = "direct-tcpip";
753 break;
754 case SSH_CHANNEL_FORWARDED_TCPIP:
755 str_subtype = "forwarded-tcpip";
756 break;
757 case (int)SSH_CHANNEL_X11:
758 str_subtype = "channel-x11";
759 break;
760 case SSH_CHANNEL_UNKNOWN:
761 /* fallthrough */
762 default:
763 str_subtype = "unknown";
764 break;
765 }
766 break;
767
768 case SSH_REQUEST_CHANNEL:
769 str_type = "request-channel";
770 switch (subtype) {
771 case SSH_CHANNEL_REQUEST_PTY:
772 str_subtype = "pty";
773 break;
774 case SSH_CHANNEL_REQUEST_EXEC:
775 str_subtype = "exec";
776 break;
777 case SSH_CHANNEL_REQUEST_SHELL:
778 str_subtype = "shell";
779 break;
780 case SSH_CHANNEL_REQUEST_ENV:
781 str_subtype = "env";
782 break;
783 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
784 str_subtype = "subsystem";
785 break;
786 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
787 str_subtype = "window-change";
788 break;
789 case SSH_CHANNEL_REQUEST_X11:
790 str_subtype = "x11";
791 break;
792 case SSH_CHANNEL_REQUEST_UNKNOWN:
793 /* fallthrough */
794 default:
795 str_subtype = "unknown";
796 break;
797 }
798 break;
799
800 case SSH_REQUEST_SERVICE:
801 str_type = "request-service";
802 str_subtype = ssh_message_service_service(msg);
803 break;
804
805 case SSH_REQUEST_GLOBAL:
806 str_type = "request-global";
807 switch (subtype) {
808 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
809 str_subtype = "tcpip-forward";
810 break;
811 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
812 str_subtype = "cancel-tcpip-forward";
813 break;
814 case SSH_GLOBAL_REQUEST_UNKNOWN:
815 /* fallthrough */
816 default:
817 str_subtype = "unknown";
818 break;
819 }
820 break;
821
822 default:
823 str_type = "unknown";
824 str_subtype = "unknown";
825 break;
826 }
827
828 VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vaskoce319162016-02-03 15:33:08 +0100829 if ((session->status == NC_STATUS_CLOSING) || (session->status == NC_STATUS_INVALID)) {
830 /* "valid" situation if, for example, receiving some auth or channel request timeouted,
831 * but we got it now, during session free */
832 VRB("SSH message arrived on a %s session, the request will be denied.",
833 (session->status == NC_STATUS_CLOSING ? "closing" : "invalid"));
834 ssh_message_reply_default(msg);
835 return 0;
836 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100837 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +0100838
839 /*
840 * process known messages
841 */
842 if (type == SSH_REQUEST_AUTH) {
843 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
844 ERR("User \"%s\" authenticated, but requested another authentication.", session->username);
845 ssh_message_reply_default(msg);
846 return 0;
847 }
848
Michal Vasko3031aae2016-01-27 16:07:18 +0100849 if (session->ssh_auth_attempts >= ((struct nc_server_ssh_opts *)session->ti_opts)->auth_attempts) {
Michal Vasko086311b2016-01-08 09:53:11 +0100850 /* too many failed attempts */
851 ssh_message_reply_default(msg);
852 return 0;
853 }
854
855 /* save the username, do not let the client change it */
856 username = ssh_message_auth_user(msg);
857 if (!session->username) {
858 if (!username) {
859 ERR("Denying an auth request without a username.");
860 return 1;
861 }
862
Michal Vasko05ba9df2016-01-13 14:40:27 +0100863 session->username = lydict_insert(server_opts.ctx, username, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100864 } else if (username) {
865 if (strcmp(username, session->username)) {
866 ERR("User \"%s\" changed its username to \"%s\".", session->username, username);
867 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +0100868 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +0100869 return 1;
870 }
871 }
872
873 if (subtype == SSH_AUTH_METHOD_NONE) {
874 /* libssh will return the supported auth methods */
875 return 1;
876 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
877 nc_sshcb_auth_password(session, msg);
878 return 0;
879 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
880 nc_sshcb_auth_pubkey(session, msg);
881 return 0;
882 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
883 nc_sshcb_auth_kbdint(session, msg);
884 return 0;
885 }
886 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +0100887 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100888 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100889 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100890 }
Michal Vasko086311b2016-01-08 09:53:11 +0100891 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +0100892
Michal Vasko0df67562016-01-21 15:50:11 +0100893 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100894 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
895 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +0100896 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +0100897 } else {
898 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100899 }
900 return 0;
901 }
902 }
903
904 /* we did not process it */
905 return 1;
906}
907
Michal Vasko1a38c862016-01-15 15:50:07 +0100908/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +0100909static int
910nc_open_netconf_channel(struct nc_session *session, int timeout)
911{
Michal Vasko105bf272016-02-03 15:34:35 +0100912 int elapsed_usec = 0, ret, elapsed;
Michal Vasko086311b2016-01-08 09:53:11 +0100913
914 /* message callback is executed twice to give chance for the channel to be
915 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100916 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +0100917 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100918 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +0100919 return -1;
920 }
921
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100922 ret = nc_timedlock(session->ti_lock, timeout, NULL);
923 if (ret != 1) {
924 return ret;
925 }
926
927 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
928 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100929 ERR("Failed to receive SSH messages on a session (%s).",
930 ssh_get_error(session->ti.libssh.session));
Michal Vasko11d142a2016-01-19 15:58:24 +0100931 pthread_mutex_unlock(session->ti_lock);
Michal Vasko086311b2016-01-08 09:53:11 +0100932 return -1;
933 }
934
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100935 if (!session->ti.libssh.channel) {
936 /* we did not receive channel-open, timeout */
937 pthread_mutex_unlock(session->ti_lock);
938 return 0;
939 }
940
941 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
942 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100943 ERR("Failed to receive SSH messages on a session (%s).",
944 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100945 pthread_mutex_unlock(session->ti_lock);
946 return -1;
947 }
948 pthread_mutex_unlock(session->ti_lock);
949
950 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
951 /* we did not receive subsystem-request, timeout */
952 return 0;
953 }
954
955 return 1;
956 }
957
958 while (1) {
959 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100960 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100961 return -1;
962 }
963
Michal Vasko105bf272016-02-03 15:34:35 +0100964 elapsed = 0;
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100965 ret = nc_timedlock(session->ti_lock, timeout, &elapsed);
966 if (ret != 1) {
967 return ret;
968 }
Michal Vasko105bf272016-02-03 15:34:35 +0100969 elapsed_usec += elapsed * 1000;
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100970
971 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
972 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100973 ERR("Failed to receive SSH messages on a session (%s).",
974 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100975 pthread_mutex_unlock(session->ti_lock);
976 return -1;
977 }
978
979 pthread_mutex_unlock(session->ti_lock);
980
Michal Vasko086311b2016-01-08 09:53:11 +0100981 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100982 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +0100983 }
984
Michal Vasko105bf272016-02-03 15:34:35 +0100985 if ((timeout != -1) && (elapsed_usec / 1000 >= timeout)) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100986 /* timeout */
Michal Vasko086311b2016-01-08 09:53:11 +0100987 break;
988 }
989
Michal Vasko086311b2016-01-08 09:53:11 +0100990 usleep(NC_TIMEOUT_STEP);
Michal Vasko105bf272016-02-03 15:34:35 +0100991 elapsed_usec += NC_TIMEOUT_STEP;
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100992 }
Michal Vasko086311b2016-01-08 09:53:11 +0100993
Michal Vasko1a38c862016-01-15 15:50:07 +0100994 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100995}
996
Michal Vasko96164bf2016-01-21 15:41:58 +0100997/* ret 0 - timeout, 1 channel has data, 2 some other channel has data,
998 * 3 status change, 4 new SSH message, 5 new NETCONF SSH channel, -1 error */
999int
1000nc_ssh_pollin(struct nc_session *session, int *timeout)
1001{
1002 int ret, elapsed = 0;
1003 struct nc_session *new;
1004
1005 ret = nc_timedlock(session->ti_lock, *timeout, &elapsed);
1006 if (*timeout > 0) {
1007 *timeout -= elapsed;
1008 }
1009
1010 if (ret != 1) {
1011 return ret;
1012 }
1013
1014 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1015 pthread_mutex_unlock(session->ti_lock);
1016
1017 if (ret != SSH_OK) {
1018 ERR("Session %u: failed to receive SSH messages (%s).", session->id,
1019 ssh_get_error(session->ti.libssh.session));
1020 session->status = NC_STATUS_INVALID;
1021 session->term_reason = NC_SESSION_TERM_OTHER;
1022 return 3;
1023 }
1024
1025 /* new SSH message */
1026 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1027 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1028 if (session->ti.libssh.next) {
1029 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1030 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1031 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1032 /* new NETCONF SSH channel */
1033 return 5;
1034 }
1035 }
1036 }
1037
1038 /* just some SSH message */
1039 return 4;
1040 }
1041
1042 /* no new SSH message, maybe NETCONF data? */
1043 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
1044 /* not this one */
1045 if (!ret) {
1046 return 2;
1047 } else if (ret == SSH_ERROR) {
1048 ERR("Session %u: SSH channel error (%s).", session->id,
1049 ssh_get_error(session->ti.libssh.session));
1050 session->status = NC_STATUS_INVALID;
1051 session->term_reason = NC_SESSION_TERM_OTHER;
1052 return 3;
1053 } else if (ret == SSH_EOF) {
1054 ERR("Session %u: communication channel unexpectedly closed (libssh).",
1055 session->id);
1056 session->status = NC_STATUS_INVALID;
1057 session->term_reason = NC_SESSION_TERM_DROPPED;
1058 return 3;
1059 }
1060
1061 return 1;
1062}
1063
Michal Vasko3031aae2016-01-27 16:07:18 +01001064API int
1065nc_connect_callhome_ssh(const char *host, uint16_t port, int timeout, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001066{
Michal Vasko3031aae2016-01-27 16:07:18 +01001067 return nc_connect_callhome(host, port, NC_TI_LIBSSH, timeout, session);
1068}
1069
1070int
1071nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
1072{
1073 struct nc_server_ssh_opts *opts;
Michal Vasko72387da2016-02-02 15:52:41 +01001074 int libssh_auth_methods = 0, elapsed_usec = 0, ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001075
Michal Vasko3031aae2016-01-27 16:07:18 +01001076 opts = session->ti_opts;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001077
Michal Vasko086311b2016-01-08 09:53:11 +01001078 /* other transport-specific data */
1079 session->ti_type = NC_TI_LIBSSH;
1080 session->ti.libssh.session = ssh_new();
1081 if (!session->ti.libssh.session) {
Michal Vaskod083db62016-01-19 10:31:29 +01001082 ERR("Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001083 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001084 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001085 }
1086
Michal Vaskoc61c4492016-01-25 11:13:34 +01001087 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001088 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1089 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001090 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001091 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1092 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001093 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001094 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1095 }
1096 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1097
1098 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001099 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001100 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001101
Michal Vaskoc61c4492016-01-25 11:13:34 +01001102 if (ssh_bind_accept_fd(opts->sshbind, session->ti.libssh.session, sock) == SSH_ERROR) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001103 ERR("SSH failed to accept a new connection (%s).", ssh_get_error(opts->sshbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001104 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001105 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001106 }
1107
1108 if (ssh_handle_key_exchange(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001109 ERR("SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001110 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001111 }
1112
1113 /* authenticate */
1114 do {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001115 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001116 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001117 return -1;
1118 }
1119
Michal Vasko086311b2016-01-08 09:53:11 +01001120 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001121 ERR("Failed to receive SSH messages on a session (%s).",
1122 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001123 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001124 }
1125
1126 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1127 break;
1128 }
1129
1130 usleep(NC_TIMEOUT_STEP);
Michal Vasko72387da2016-02-02 15:52:41 +01001131 elapsed_usec += NC_TIMEOUT_STEP;
1132 } while ((timeout == -1) || (timeout && (elapsed_usec / 1000 < timeout)));
Michal Vasko086311b2016-01-08 09:53:11 +01001133
1134 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1135 /* timeout */
Michal Vasko1a38c862016-01-15 15:50:07 +01001136 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001137 }
1138
1139 if (timeout > 0) {
Michal Vasko72387da2016-02-02 15:52:41 +01001140 timeout -= elapsed_usec / 1000;
Michal Vasko086311b2016-01-08 09:53:11 +01001141 }
1142
1143 /* open channel */
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001144 ret = nc_open_netconf_channel(session, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001145 if (ret < 1) {
1146 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001147 }
1148
Michal Vasko96164bf2016-01-21 15:41:58 +01001149 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1150
Michal Vasko1a38c862016-01-15 15:50:07 +01001151 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001152}
1153
Michal Vasko96164bf2016-01-21 15:41:58 +01001154API int
1155nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001156{
Michal Vasko96164bf2016-01-21 15:41:58 +01001157 struct nc_session *new_session = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001158 uint16_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001159
Michal Vasko96164bf2016-01-21 15:41:58 +01001160 if (!ps || !session) {
1161 ERRARG;
1162 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001163 }
1164
Michal Vasko96164bf2016-01-21 15:41:58 +01001165 for (i = 0; i < ps->session_count; ++i) {
1166 if ((ps->sessions[i]->status == NC_STATUS_RUNNING) && (ps->sessions[i]->ti_type == NC_TI_LIBSSH)
1167 && ps->sessions[i]->ti.libssh.next) {
1168 /* an SSH session with more channels */
1169 for (new_session = ps->sessions[i]->ti.libssh.next;
1170 new_session != ps->sessions[i];
1171 new_session = new_session->ti.libssh.next) {
1172 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1173 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1174 /* we found our session */
1175 break;
1176 }
1177 }
1178 if (new_session != ps->sessions[i]) {
1179 break;
1180 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001181
Michal Vasko96164bf2016-01-21 15:41:58 +01001182 new_session = NULL;
1183 }
1184 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001185
Michal Vasko96164bf2016-01-21 15:41:58 +01001186 if (!new_session) {
1187 ERR("No session with a NETCONF SSH channel ready was found.");
1188 return -1;
1189 }
1190
1191 /* assign new SID atomically */
1192 pthread_spin_lock(&server_opts.sid_lock);
1193 new_session->id = server_opts.new_session_id++;
1194 pthread_spin_unlock(&server_opts.sid_lock);
Michal Vaskofb89d772016-01-08 12:25:35 +01001195
Michal Vasko086311b2016-01-08 09:53:11 +01001196 /* NETCONF handshake */
1197 if (nc_handshake(new_session)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001198 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001199 }
1200 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001201 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001202
Michal Vasko96164bf2016-01-21 15:41:58 +01001203 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001204}