blob: 30678131ba76490153a25663f5d5f903d3629717 [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 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;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100316 opts->authkeys = nc_realloc(opts->authkeys, opts->authkey_count * sizeof *opts->authkeys);
317 if (!opts->authkeys) {
318 ERRMEM;
319 return -1;
320 }
Michal Vaskob05053d2016-01-22 16:12:06 +0100321 opts->authkeys[opts->authkey_count - 1].path = lydict_insert(server_opts.ctx, pubkey_path, 0);
322 opts->authkeys[opts->authkey_count - 1].username = lydict_insert(server_opts.ctx, username, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +0100323
Michal Vasko086311b2016-01-08 09:53:11 +0100324 return 0;
325}
326
327API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100328nc_server_ssh_endpt_add_authkey(const char *endpt_name, const char *pubkey_path, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100329{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100330 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100331 struct nc_endpt *endpt;
332
Michal Vasko51e514d2016-02-02 15:51:52 +0100333 /* LOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100334 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100335 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100336 return -1;
337 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100338 ret = nc_server_ssh_add_authkey(pubkey_path, username, endpt->ti_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100339 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100340 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100341
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100342 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100343}
344
345API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100346nc_server_ssh_ch_add_authkey(const char *pubkey_path, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100347{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100348 int ret;
349
350 /* OPTS LOCK */
351 pthread_mutex_lock(&ssh_ch_opts_lock);
352 ret = nc_server_ssh_add_authkey(pubkey_path, username, &ssh_ch_opts);
353 /* OPTS UNLOCK */
354 pthread_mutex_unlock(&ssh_ch_opts_lock);
355
356 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100357}
358
359static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100360nc_server_ssh_del_authkey(const char *pubkey_path, const char *username, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100361{
Michal Vasko086311b2016-01-08 09:53:11 +0100362 uint32_t i;
363 int ret = -1;
364
Michal Vasko1a38c862016-01-15 15:50:07 +0100365 if (!pubkey_path && !username) {
Michal Vaskob05053d2016-01-22 16:12:06 +0100366 for (i = 0; i < opts->authkey_count; ++i) {
367 lydict_remove(server_opts.ctx, opts->authkeys[i].path);
368 lydict_remove(server_opts.ctx, opts->authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100369
Michal Vasko086311b2016-01-08 09:53:11 +0100370 ret = 0;
371 }
Michal Vaskob05053d2016-01-22 16:12:06 +0100372 free(opts->authkeys);
373 opts->authkeys = NULL;
374 opts->authkey_count = 0;
Michal Vasko1a38c862016-01-15 15:50:07 +0100375 } else {
Michal Vaskob05053d2016-01-22 16:12:06 +0100376 for (i = 0; i < opts->authkey_count; ++i) {
377 if ((!pubkey_path || !strcmp(opts->authkeys[i].path, pubkey_path))
378 && (!username || !strcmp(opts->authkeys[i].username, username))) {
Michal Vaskob05053d2016-01-22 16:12:06 +0100379 lydict_remove(server_opts.ctx, opts->authkeys[i].path);
380 lydict_remove(server_opts.ctx, opts->authkeys[i].username);
Michal Vasko1a38c862016-01-15 15:50:07 +0100381
Michal Vaskob05053d2016-01-22 16:12:06 +0100382 --opts->authkey_count;
Michal Vaskoc0256492016-02-02 12:19:06 +0100383 if (i < opts->authkey_count) {
384 memcpy(&opts->authkeys[i], &opts->authkeys[opts->authkey_count], sizeof *opts->authkeys);
385 } else if (!opts->authkey_count) {
386 free(opts->authkeys);
387 opts->authkeys = NULL;
388 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100389
390 ret = 0;
391 }
392 }
Michal Vasko086311b2016-01-08 09:53:11 +0100393 }
394
395 return ret;
396}
397
Michal Vaskob05053d2016-01-22 16:12:06 +0100398API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100399nc_server_ssh_endpt_del_authkey(const char *endpt_name, const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100400{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100401 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100402 struct nc_endpt *endpt;
403
Michal Vasko51e514d2016-02-02 15:51:52 +0100404 /* LOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100405 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100406 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100407 return -1;
408 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100409 ret = nc_server_ssh_del_authkey(pubkey_path, username, endpt->ti_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100410 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100411 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100412
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100413 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100414}
415
416API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100417nc_server_ssh_ch_del_authkey(const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100418{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100419 int ret;
420
421 /* OPTS LOCK */
422 pthread_mutex_lock(&ssh_ch_opts_lock);
423 ret = nc_server_ssh_del_authkey(pubkey_path, username, &ssh_ch_opts);
424 /* OPTS UNLOCK */
425 pthread_mutex_unlock(&ssh_ch_opts_lock);
426
427 return ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100428}
429
430void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100431nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100432{
433 if (opts->sshbind) {
434 ssh_bind_free(opts->sshbind);
435 opts->sshbind = NULL;
436 }
437
438 nc_server_ssh_del_authkey(NULL, NULL, opts);
Michal Vaskob05053d2016-01-22 16:12:06 +0100439}
440
Michal Vasko086311b2016-01-08 09:53:11 +0100441API void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100442nc_server_ssh_ch_clear_opts(void)
Michal Vasko086311b2016-01-08 09:53:11 +0100443{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100444 /* OPTS LOCK */
445 pthread_mutex_lock(&ssh_ch_opts_lock);
446 nc_server_ssh_clear_opts(&ssh_ch_opts);
447 /* OPTS UNLOCK */
448 pthread_mutex_unlock(&ssh_ch_opts_lock);
Michal Vasko086311b2016-01-08 09:53:11 +0100449}
450
451static char *
452auth_password_get_pwd_hash(const char *username)
453{
454 struct passwd *pwd, pwd_buf;
455 struct spwd *spwd, spwd_buf;
456 char *pass_hash = NULL, buf[256];
457
458 getpwnam_r(username, &pwd_buf, buf, 256, &pwd);
459 if (!pwd) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100460 VRB("User \"%s\" not found locally.", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100461 return NULL;
462 }
463
464 if (!strcmp(pwd->pw_passwd, "x")) {
465 getspnam_r(username, &spwd_buf, buf, 256, &spwd);
466 if (!spwd) {
467 VRB("Failed to retrieve the shadow entry for \"%s\".", username);
468 return NULL;
469 }
470
471 pass_hash = spwd->sp_pwdp;
472 } else {
473 pass_hash = pwd->pw_passwd;
474 }
475
476 if (!pass_hash) {
Michal Vaskod083db62016-01-19 10:31:29 +0100477 ERR("No password could be retrieved for \"%s\".", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100478 return NULL;
479 }
480
481 /* check the hash structure for special meaning */
482 if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) {
483 VRB("User \"%s\" is not allowed to authenticate using a password.", username);
484 return NULL;
485 }
486 if (!strcmp(pass_hash, "*NP*")) {
487 VRB("Retrieving password for \"%s\" from a NIS+ server not supported.", username);
488 return NULL;
489 }
490
491 return strdup(pass_hash);
492}
493
494static int
495auth_password_compare_pwd(const char *pass_hash, const char *pass_clear)
496{
497 char *new_pass_hash;
498 struct crypt_data cdata;
499
500 if (!pass_hash[0]) {
501 if (!pass_clear[0]) {
502 WRN("User authentication successful with an empty password!");
503 return 0;
504 } else {
505 /* the user did now know he does not need any password,
506 * (which should not be used) so deny authentication */
507 return 1;
508 }
509 }
510
511 cdata.initialized = 0;
512 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
513 return strcmp(new_pass_hash, pass_hash);
514}
515
516static void
517nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
518{
519 char *pass_hash;
520
521 pass_hash = auth_password_get_pwd_hash(session->username);
522 if (pass_hash && !auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100523 VRB("User \"%s\" authenticated.", session->username);
Michal Vasko086311b2016-01-08 09:53:11 +0100524 ssh_message_auth_reply_success(msg, 0);
525 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
526 free(pass_hash);
527 return;
528 }
529
530 free(pass_hash);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100531 ++session->ssh_auth_attempts;
Michal Vaskod083db62016-01-19 10:31:29 +0100532 VRB("Failed user \"'%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100533 ssh_message_reply_default(msg);
534}
535
536static void
537nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
538{
539 char *pass_hash;
540
541 if (!ssh_message_auth_kbdint_is_response(msg)) {
542 const char *prompts[] = {"Password: "};
543 char echo[] = {0};
544
545 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
546 } else {
547 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {
548 ssh_message_reply_default(msg);
549 return;
550 }
551 pass_hash = auth_password_get_pwd_hash(session->username);
552 if (!pass_hash) {
553 ssh_message_reply_default(msg);
554 return;
555 }
556 if (!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0))) {
557 VRB("User \"%s\" authenticated.", session->username);
558 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
559 ssh_message_auth_reply_success(msg, 0);
560 } else {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100561 ++session->ssh_auth_attempts;
562 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100563 ssh_message_reply_default(msg);
564 }
Radek Krejcifb533742016-03-04 15:12:54 +0100565 free(pass_hash);
Michal Vasko086311b2016-01-08 09:53:11 +0100566 }
567}
568
569static const char *
Michal Vasko3031aae2016-01-27 16:07:18 +0100570auth_pubkey_compare_key(struct nc_server_ssh_opts *opts, ssh_key key)
Michal Vasko086311b2016-01-08 09:53:11 +0100571{
572 uint32_t i;
573 ssh_key pub_key;
Michal Vasko76e3a352016-01-18 09:07:00 +0100574 const char *username = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100575
Michal Vasko3031aae2016-01-27 16:07:18 +0100576 for (i = 0; i < opts->authkey_count; ++i) {
577 if (ssh_pki_import_pubkey_file(opts->authkeys[i].path, &pub_key) != SSH_OK) {
578 if (eaccess(opts->authkeys[i].path, R_OK)) {
579 WRN("Failed to import the public key \"%s\" (%s).", opts->authkeys[i].path, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100580 } else {
Michal Vaskoe5612cf2016-02-02 15:52:16 +0100581 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 +0100582 }
583 continue;
584 }
585
586 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
587 ssh_key_free(pub_key);
588 break;
589 }
590
591 ssh_key_free(pub_key);
592 }
593
Michal Vasko3031aae2016-01-27 16:07:18 +0100594 if (i < opts->authkey_count) {
595 username = opts->authkeys[i].username;
Michal Vasko086311b2016-01-08 09:53:11 +0100596 }
597
598 return username;
599}
600
601static void
602nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
603{
604 const char *username;
605 int signature_state;
606
607 signature_state = ssh_message_auth_publickey_state(msg);
608 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
609 VRB("User \"%s\" authenticated.", session->username);
610 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
611 ssh_message_auth_reply_success(msg, 0);
612 return;
613
614 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Michal Vasko2cc4c682016-03-01 09:16:48 +0100615 if ((username = auth_pubkey_compare_key(session->data, ssh_message_auth_pubkey(msg))) == NULL) {
Michal Vasko086311b2016-01-08 09:53:11 +0100616 VRB("User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
617
618 } else if (strcmp(session->username, username)) {
619 VRB("User \"%s\" is not the username identified with the presented public key.", session->username);
620
621 } else {
622 /* accepting only the use of a public key */
623 ssh_message_auth_reply_pk_ok_simple(msg);
624 return;
625 }
626 }
627
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100628 ++session->ssh_auth_attempts;
629 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100630 ssh_message_reply_default(msg);
631}
632
633static int
Michal Vasko96164bf2016-01-21 15:41:58 +0100634nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100635{
Michal Vasko96164bf2016-01-21 15:41:58 +0100636 ssh_channel chan;
637
638 /* first channel request */
639 if (!session->ti.libssh.channel) {
640 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100641 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100642 return -1;
643 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100644 chan = ssh_message_channel_request_open_reply_accept(msg);
645 if (!chan) {
646 ERR("Failed to create a new SSH channel.");
647 return -1;
648 }
649 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +0100650
Michal Vasko96164bf2016-01-21 15:41:58 +0100651 /* additional channel request */
652 } else {
653 chan = ssh_message_channel_request_open_reply_accept(msg);
654 if (!chan) {
655 ERR("Session %u: failed to create a new SSH channel.", session->id);
656 return -1;
657 }
658 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +0100659 }
660
Michal Vasko086311b2016-01-08 09:53:11 +0100661 return 0;
662}
663
664static int
665nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
666{
Michal Vasko96164bf2016-01-21 15:41:58 +0100667 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +0100668
Michal Vasko96164bf2016-01-21 15:41:58 +0100669 if (strcmp(subsystem, "netconf")) {
670 WRN("Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +0100671 return -1;
672 }
673
Michal Vasko96164bf2016-01-21 15:41:58 +0100674 if (session->ti.libssh.channel == channel) {
675 /* first channel requested */
676 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
677 ERRINT;
678 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100679 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100680 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
681 ERR("Session %u: subsystem \"netconf\" requested for the second time.", session->id);
682 return -1;
683 }
684
685 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +0100686 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +0100687 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
688 new_session = calloc(1, sizeof *new_session);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100689 if (!new_session) {
690 ERRMEM;
691 return -1;
692 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100693
694 /* insert the new session */
695 if (!session->ti.libssh.next) {
696 new_session->ti.libssh.next = session;
697 } else {
698 new_session->ti.libssh.next = session->ti.libssh.next;
699 }
700 session->ti.libssh.next = new_session;
701
702 new_session->status = NC_STATUS_STARTING;
703 new_session->side = NC_SERVER;
704 new_session->ti_type = NC_TI_LIBSSH;
705 new_session->ti_lock = session->ti_lock;
706 new_session->ti.libssh.channel = channel;
707 new_session->ti.libssh.session = session->ti.libssh.session;
708 new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
709 new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
710 new_session->port = session->port;
711 new_session->ctx = server_opts.ctx;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100712 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX
713 | (session->flags & NC_SESSION_CALLHOME ? NC_SESSION_CALLHOME : 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100714 }
715
716 return 0;
717}
718
Michal Vasko96164bf2016-01-21 15:41:58 +0100719int
Michal Vaskob48aa812016-01-18 14:13:09 +0100720nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +0100721{
722 const char *str_type, *str_subtype = NULL, *username;
723 int subtype, type;
724 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +0100725
726 type = ssh_message_type(msg);
727 subtype = ssh_message_subtype(msg);
728
729 switch (type) {
730 case SSH_REQUEST_AUTH:
731 str_type = "request-auth";
732 switch (subtype) {
733 case SSH_AUTH_METHOD_NONE:
734 str_subtype = "none";
735 break;
736 case SSH_AUTH_METHOD_PASSWORD:
737 str_subtype = "password";
738 break;
739 case SSH_AUTH_METHOD_PUBLICKEY:
740 str_subtype = "publickey";
741 break;
742 case SSH_AUTH_METHOD_HOSTBASED:
743 str_subtype = "hostbased";
744 break;
745 case SSH_AUTH_METHOD_INTERACTIVE:
746 str_subtype = "interactive";
747 break;
748 case SSH_AUTH_METHOD_GSSAPI_MIC:
749 str_subtype = "gssapi-mic";
750 break;
751 }
752 break;
753
754 case SSH_REQUEST_CHANNEL_OPEN:
755 str_type = "request-channel-open";
756 switch (subtype) {
757 case SSH_CHANNEL_SESSION:
758 str_subtype = "session";
759 break;
760 case SSH_CHANNEL_DIRECT_TCPIP:
761 str_subtype = "direct-tcpip";
762 break;
763 case SSH_CHANNEL_FORWARDED_TCPIP:
764 str_subtype = "forwarded-tcpip";
765 break;
766 case (int)SSH_CHANNEL_X11:
767 str_subtype = "channel-x11";
768 break;
769 case SSH_CHANNEL_UNKNOWN:
770 /* fallthrough */
771 default:
772 str_subtype = "unknown";
773 break;
774 }
775 break;
776
777 case SSH_REQUEST_CHANNEL:
778 str_type = "request-channel";
779 switch (subtype) {
780 case SSH_CHANNEL_REQUEST_PTY:
781 str_subtype = "pty";
782 break;
783 case SSH_CHANNEL_REQUEST_EXEC:
784 str_subtype = "exec";
785 break;
786 case SSH_CHANNEL_REQUEST_SHELL:
787 str_subtype = "shell";
788 break;
789 case SSH_CHANNEL_REQUEST_ENV:
790 str_subtype = "env";
791 break;
792 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
793 str_subtype = "subsystem";
794 break;
795 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
796 str_subtype = "window-change";
797 break;
798 case SSH_CHANNEL_REQUEST_X11:
799 str_subtype = "x11";
800 break;
801 case SSH_CHANNEL_REQUEST_UNKNOWN:
802 /* fallthrough */
803 default:
804 str_subtype = "unknown";
805 break;
806 }
807 break;
808
809 case SSH_REQUEST_SERVICE:
810 str_type = "request-service";
811 str_subtype = ssh_message_service_service(msg);
812 break;
813
814 case SSH_REQUEST_GLOBAL:
815 str_type = "request-global";
816 switch (subtype) {
817 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
818 str_subtype = "tcpip-forward";
819 break;
820 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
821 str_subtype = "cancel-tcpip-forward";
822 break;
823 case SSH_GLOBAL_REQUEST_UNKNOWN:
824 /* fallthrough */
825 default:
826 str_subtype = "unknown";
827 break;
828 }
829 break;
830
831 default:
832 str_type = "unknown";
833 str_subtype = "unknown";
834 break;
835 }
836
837 VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vaskoce319162016-02-03 15:33:08 +0100838 if ((session->status == NC_STATUS_CLOSING) || (session->status == NC_STATUS_INVALID)) {
839 /* "valid" situation if, for example, receiving some auth or channel request timeouted,
840 * but we got it now, during session free */
841 VRB("SSH message arrived on a %s session, the request will be denied.",
842 (session->status == NC_STATUS_CLOSING ? "closing" : "invalid"));
843 ssh_message_reply_default(msg);
844 return 0;
845 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100846 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +0100847
848 /*
849 * process known messages
850 */
851 if (type == SSH_REQUEST_AUTH) {
852 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
853 ERR("User \"%s\" authenticated, but requested another authentication.", session->username);
854 ssh_message_reply_default(msg);
855 return 0;
856 }
857
Michal Vasko2cc4c682016-03-01 09:16:48 +0100858 if (session->ssh_auth_attempts >= ((struct nc_server_ssh_opts *)session->data)->auth_attempts) {
Michal Vasko086311b2016-01-08 09:53:11 +0100859 /* too many failed attempts */
860 ssh_message_reply_default(msg);
861 return 0;
862 }
863
864 /* save the username, do not let the client change it */
865 username = ssh_message_auth_user(msg);
866 if (!session->username) {
867 if (!username) {
868 ERR("Denying an auth request without a username.");
869 return 1;
870 }
871
Michal Vasko05ba9df2016-01-13 14:40:27 +0100872 session->username = lydict_insert(server_opts.ctx, username, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100873 } else if (username) {
874 if (strcmp(username, session->username)) {
875 ERR("User \"%s\" changed its username to \"%s\".", session->username, username);
876 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +0100877 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +0100878 return 1;
879 }
880 }
881
882 if (subtype == SSH_AUTH_METHOD_NONE) {
883 /* libssh will return the supported auth methods */
884 return 1;
885 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
886 nc_sshcb_auth_password(session, msg);
887 return 0;
888 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
889 nc_sshcb_auth_pubkey(session, msg);
890 return 0;
891 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
892 nc_sshcb_auth_kbdint(session, msg);
893 return 0;
894 }
895 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +0100896 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100897 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100898 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100899 }
Michal Vasko086311b2016-01-08 09:53:11 +0100900 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +0100901
Michal Vasko0df67562016-01-21 15:50:11 +0100902 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100903 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
904 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +0100905 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +0100906 } else {
907 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100908 }
909 return 0;
910 }
911 }
912
913 /* we did not process it */
914 return 1;
915}
916
Michal Vasko1a38c862016-01-15 15:50:07 +0100917/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +0100918static int
919nc_open_netconf_channel(struct nc_session *session, int timeout)
920{
Michal Vasko62be1ce2016-03-03 13:24:52 +0100921 int elapsed_usec = 0, ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100922
923 /* message callback is executed twice to give chance for the channel to be
924 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100925 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +0100926 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100927 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +0100928 return -1;
929 }
930
Michal Vasko62be1ce2016-03-03 13:24:52 +0100931 ret = nc_timedlock(session->ti_lock, timeout);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100932 if (ret != 1) {
933 return ret;
934 }
935
936 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
937 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100938 ERR("Failed to receive SSH messages on a session (%s).",
939 ssh_get_error(session->ti.libssh.session));
Michal Vasko11d142a2016-01-19 15:58:24 +0100940 pthread_mutex_unlock(session->ti_lock);
Michal Vasko086311b2016-01-08 09:53:11 +0100941 return -1;
942 }
943
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100944 if (!session->ti.libssh.channel) {
945 /* we did not receive channel-open, timeout */
946 pthread_mutex_unlock(session->ti_lock);
947 return 0;
948 }
949
950 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
951 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100952 ERR("Failed to receive SSH messages on a session (%s).",
953 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100954 pthread_mutex_unlock(session->ti_lock);
955 return -1;
956 }
957 pthread_mutex_unlock(session->ti_lock);
958
959 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
960 /* we did not receive subsystem-request, timeout */
961 return 0;
962 }
963
964 return 1;
965 }
966
967 while (1) {
968 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100969 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100970 return -1;
971 }
972
Michal Vasko62be1ce2016-03-03 13:24:52 +0100973 ret = nc_timedlock(session->ti_lock, timeout);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100974 if (ret != 1) {
975 return ret;
976 }
977
978 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
979 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100980 ERR("Failed to receive SSH messages on a session (%s).",
981 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100982 pthread_mutex_unlock(session->ti_lock);
983 return -1;
984 }
985
986 pthread_mutex_unlock(session->ti_lock);
987
Michal Vasko086311b2016-01-08 09:53:11 +0100988 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100989 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +0100990 }
991
Michal Vasko105bf272016-02-03 15:34:35 +0100992 if ((timeout != -1) && (elapsed_usec / 1000 >= timeout)) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100993 /* timeout */
Michal Vasko842522c2016-03-01 09:20:13 +0100994 ERR("Failed to start \"netconf\" SSH subsystem for too long, disconnecting.");
Michal Vasko086311b2016-01-08 09:53:11 +0100995 break;
996 }
997
Michal Vasko086311b2016-01-08 09:53:11 +0100998 usleep(NC_TIMEOUT_STEP);
Michal Vasko105bf272016-02-03 15:34:35 +0100999 elapsed_usec += NC_TIMEOUT_STEP;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001000 }
Michal Vasko086311b2016-01-08 09:53:11 +01001001
Michal Vasko1a38c862016-01-15 15:50:07 +01001002 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001003}
1004
Michal Vasko96164bf2016-01-21 15:41:58 +01001005/* ret 0 - timeout, 1 channel has data, 2 some other channel has data,
1006 * 3 status change, 4 new SSH message, 5 new NETCONF SSH channel, -1 error */
1007int
Michal Vasko62be1ce2016-03-03 13:24:52 +01001008nc_ssh_pollin(struct nc_session *session, int timeout)
Michal Vasko96164bf2016-01-21 15:41:58 +01001009{
Michal Vasko62be1ce2016-03-03 13:24:52 +01001010 int ret;
Michal Vasko96164bf2016-01-21 15:41:58 +01001011 struct nc_session *new;
1012
Michal Vasko62be1ce2016-03-03 13:24:52 +01001013 ret = nc_timedlock(session->ti_lock, timeout);
Michal Vasko96164bf2016-01-21 15:41:58 +01001014
1015 if (ret != 1) {
1016 return ret;
1017 }
1018
1019 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1020 pthread_mutex_unlock(session->ti_lock);
1021
1022 if (ret != SSH_OK) {
1023 ERR("Session %u: failed to receive SSH messages (%s).", session->id,
1024 ssh_get_error(session->ti.libssh.session));
1025 session->status = NC_STATUS_INVALID;
1026 session->term_reason = NC_SESSION_TERM_OTHER;
1027 return 3;
1028 }
1029
1030 /* new SSH message */
1031 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1032 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1033 if (session->ti.libssh.next) {
1034 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1035 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1036 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1037 /* new NETCONF SSH channel */
1038 return 5;
1039 }
1040 }
1041 }
1042
1043 /* just some SSH message */
1044 return 4;
1045 }
1046
1047 /* no new SSH message, maybe NETCONF data? */
1048 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
1049 /* not this one */
1050 if (!ret) {
1051 return 2;
1052 } else if (ret == SSH_ERROR) {
1053 ERR("Session %u: SSH channel error (%s).", session->id,
1054 ssh_get_error(session->ti.libssh.session));
1055 session->status = NC_STATUS_INVALID;
1056 session->term_reason = NC_SESSION_TERM_OTHER;
1057 return 3;
1058 } else if (ret == SSH_EOF) {
1059 ERR("Session %u: communication channel unexpectedly closed (libssh).",
1060 session->id);
1061 session->status = NC_STATUS_INVALID;
1062 session->term_reason = NC_SESSION_TERM_DROPPED;
1063 return 3;
1064 }
1065
1066 return 1;
1067}
1068
Michal Vasko3031aae2016-01-27 16:07:18 +01001069API int
Michal Vasko8f5270d2016-02-29 16:22:25 +01001070nc_connect_callhome_ssh(const char *host, uint16_t port, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001071{
Michal Vasko8f5270d2016-02-29 16:22:25 +01001072 return nc_connect_callhome(host, port, NC_TI_LIBSSH, session);
Michal Vasko3031aae2016-01-27 16:07:18 +01001073}
1074
1075int
Michal Vasko0190bc32016-03-02 15:47:49 +01001076nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko3031aae2016-01-27 16:07:18 +01001077{
1078 struct nc_server_ssh_opts *opts;
Michal Vasko72387da2016-02-02 15:52:41 +01001079 int libssh_auth_methods = 0, elapsed_usec = 0, ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001080
Michal Vasko2cc4c682016-03-01 09:16:48 +01001081 opts = session->data;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001082
Michal Vasko086311b2016-01-08 09:53:11 +01001083 /* other transport-specific data */
1084 session->ti_type = NC_TI_LIBSSH;
1085 session->ti.libssh.session = ssh_new();
1086 if (!session->ti.libssh.session) {
Michal Vaskod083db62016-01-19 10:31:29 +01001087 ERR("Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001088 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001089 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001090 }
1091
Michal Vaskoc61c4492016-01-25 11:13:34 +01001092 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001093 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1094 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001095 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001096 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1097 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001098 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001099 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1100 }
1101 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1102
1103 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001104 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001105 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001106
Michal Vaskoc61c4492016-01-25 11:13:34 +01001107 if (ssh_bind_accept_fd(opts->sshbind, session->ti.libssh.session, sock) == SSH_ERROR) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001108 ERR("SSH failed to accept a new connection (%s).", ssh_get_error(opts->sshbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001109 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001110 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001111 }
1112
Michal Vasko0190bc32016-03-02 15:47:49 +01001113 ssh_set_blocking(session->ti.libssh.session, 0);
1114
1115 while ((ret = ssh_handle_key_exchange(session->ti.libssh.session)) == SSH_AGAIN) {
Michal Vaskoe65b4492016-03-03 11:57:51 +01001116 /* this tends to take longer */
1117 usleep(NC_TIMEOUT_STEP * 20);
1118 elapsed_usec += NC_TIMEOUT_STEP * 20;
Michal Vasko0190bc32016-03-02 15:47:49 +01001119 if ((timeout > -1) && (elapsed_usec / 1000 >= timeout)) {
1120 break;
1121 }
1122 }
1123 if (ret == SSH_AGAIN) {
1124 ERR("SSH key exchange timeout.");
1125 return 0;
1126 } else if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001127 ERR("SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001128 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001129 }
1130
1131 /* authenticate */
Michal Vasko0190bc32016-03-02 15:47:49 +01001132 elapsed_usec = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001133 do {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001134 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001135 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001136 return -1;
1137 }
1138
Michal Vasko086311b2016-01-08 09:53:11 +01001139 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001140 ERR("Failed to receive SSH messages on a session (%s).",
1141 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001142 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001143 }
1144
1145 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1146 break;
1147 }
1148
1149 usleep(NC_TIMEOUT_STEP);
Michal Vasko72387da2016-02-02 15:52:41 +01001150 elapsed_usec += NC_TIMEOUT_STEP;
Michal Vaskoab639942016-02-29 16:23:47 +01001151 } while (!opts->auth_timeout || (elapsed_usec / 1000000 < opts->auth_timeout));
Michal Vasko086311b2016-01-08 09:53:11 +01001152
1153 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1154 /* timeout */
Michal Vasko842522c2016-03-01 09:20:13 +01001155 ERR("Client failed to authenticate for too long, disconnecting.");
Michal Vasko1a38c862016-01-15 15:50:07 +01001156 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001157 }
1158
Michal Vasko086311b2016-01-08 09:53:11 +01001159 /* open channel */
Michal Vaskoab639942016-02-29 16:23:47 +01001160 ret = nc_open_netconf_channel(session, opts->auth_timeout ? (opts->auth_timeout * 1000 - elapsed_usec / 1000) : -1);
Michal Vasko1a38c862016-01-15 15:50:07 +01001161 if (ret < 1) {
1162 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001163 }
1164
Michal Vasko96164bf2016-01-21 15:41:58 +01001165 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1166
Michal Vasko1a38c862016-01-15 15:50:07 +01001167 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001168}
1169
Michal Vasko96164bf2016-01-21 15:41:58 +01001170API int
1171nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001172{
Michal Vasko96164bf2016-01-21 15:41:58 +01001173 struct nc_session *new_session = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001174 uint16_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001175
Michal Vasko96164bf2016-01-21 15:41:58 +01001176 if (!ps || !session) {
1177 ERRARG;
1178 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001179 }
1180
Michal Vasko48a63ed2016-03-01 09:48:21 +01001181 /* LOCK */
Michal Vaskof04a52a2016-04-07 10:52:10 +02001182 if (nc_ps_lock(ps)) {
1183 return -1;
1184 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001185
Michal Vasko96164bf2016-01-21 15:41:58 +01001186 for (i = 0; i < ps->session_count; ++i) {
1187 if ((ps->sessions[i]->status == NC_STATUS_RUNNING) && (ps->sessions[i]->ti_type == NC_TI_LIBSSH)
1188 && ps->sessions[i]->ti.libssh.next) {
1189 /* an SSH session with more channels */
1190 for (new_session = ps->sessions[i]->ti.libssh.next;
1191 new_session != ps->sessions[i];
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 != ps->sessions[i]) {
1200 break;
1201 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001202
Michal Vasko96164bf2016-01-21 15:41:58 +01001203 new_session = NULL;
1204 }
1205 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001206
Michal Vasko48a63ed2016-03-01 09:48:21 +01001207 /* UNLOCK */
Michal Vaskof04a52a2016-04-07 10:52:10 +02001208 nc_ps_unlock(ps);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001209
Michal Vasko96164bf2016-01-21 15:41:58 +01001210 if (!new_session) {
1211 ERR("No session with a NETCONF SSH channel ready was found.");
1212 return -1;
1213 }
1214
1215 /* assign new SID atomically */
1216 pthread_spin_lock(&server_opts.sid_lock);
1217 new_session->id = server_opts.new_session_id++;
1218 pthread_spin_unlock(&server_opts.sid_lock);
Michal Vaskofb89d772016-01-08 12:25:35 +01001219
Michal Vasko086311b2016-01-08 09:53:11 +01001220 /* NETCONF handshake */
1221 if (nc_handshake(new_session)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001222 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001223 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001224
Michal Vasko086311b2016-01-08 09:53:11 +01001225 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001226 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001227
Michal Vasko96164bf2016-01-21 15:41:58 +01001228 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001229}