blob: ad54711464c97d576e8803d4996e23f366d8092e [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 }
565 }
566}
567
568static const char *
Michal Vasko3031aae2016-01-27 16:07:18 +0100569auth_pubkey_compare_key(struct nc_server_ssh_opts *opts, ssh_key key)
Michal Vasko086311b2016-01-08 09:53:11 +0100570{
571 uint32_t i;
572 ssh_key pub_key;
Michal Vasko76e3a352016-01-18 09:07:00 +0100573 const char *username = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100574
Michal Vasko3031aae2016-01-27 16:07:18 +0100575 for (i = 0; i < opts->authkey_count; ++i) {
576 if (ssh_pki_import_pubkey_file(opts->authkeys[i].path, &pub_key) != SSH_OK) {
577 if (eaccess(opts->authkeys[i].path, R_OK)) {
578 WRN("Failed to import the public key \"%s\" (%s).", opts->authkeys[i].path, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100579 } else {
Michal Vaskoe5612cf2016-02-02 15:52:16 +0100580 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 +0100581 }
582 continue;
583 }
584
585 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
586 ssh_key_free(pub_key);
587 break;
588 }
589
590 ssh_key_free(pub_key);
591 }
592
Michal Vasko3031aae2016-01-27 16:07:18 +0100593 if (i < opts->authkey_count) {
594 username = opts->authkeys[i].username;
Michal Vasko086311b2016-01-08 09:53:11 +0100595 }
596
597 return username;
598}
599
600static void
601nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
602{
603 const char *username;
604 int signature_state;
605
606 signature_state = ssh_message_auth_publickey_state(msg);
607 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
608 VRB("User \"%s\" authenticated.", session->username);
609 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
610 ssh_message_auth_reply_success(msg, 0);
611 return;
612
613 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Michal Vasko2cc4c682016-03-01 09:16:48 +0100614 if ((username = auth_pubkey_compare_key(session->data, ssh_message_auth_pubkey(msg))) == NULL) {
Michal Vasko086311b2016-01-08 09:53:11 +0100615 VRB("User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
616
617 } else if (strcmp(session->username, username)) {
618 VRB("User \"%s\" is not the username identified with the presented public key.", session->username);
619
620 } else {
621 /* accepting only the use of a public key */
622 ssh_message_auth_reply_pk_ok_simple(msg);
623 return;
624 }
625 }
626
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100627 ++session->ssh_auth_attempts;
628 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100629 ssh_message_reply_default(msg);
630}
631
632static int
Michal Vasko96164bf2016-01-21 15:41:58 +0100633nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100634{
Michal Vasko96164bf2016-01-21 15:41:58 +0100635 ssh_channel chan;
636
637 /* first channel request */
638 if (!session->ti.libssh.channel) {
639 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100640 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100641 return -1;
642 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100643 chan = ssh_message_channel_request_open_reply_accept(msg);
644 if (!chan) {
645 ERR("Failed to create a new SSH channel.");
646 return -1;
647 }
648 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +0100649
Michal Vasko96164bf2016-01-21 15:41:58 +0100650 /* additional channel request */
651 } else {
652 chan = ssh_message_channel_request_open_reply_accept(msg);
653 if (!chan) {
654 ERR("Session %u: failed to create a new SSH channel.", session->id);
655 return -1;
656 }
657 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +0100658 }
659
Michal Vasko086311b2016-01-08 09:53:11 +0100660 return 0;
661}
662
663static int
664nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
665{
Michal Vasko96164bf2016-01-21 15:41:58 +0100666 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +0100667
Michal Vasko96164bf2016-01-21 15:41:58 +0100668 if (strcmp(subsystem, "netconf")) {
669 WRN("Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +0100670 return -1;
671 }
672
Michal Vasko96164bf2016-01-21 15:41:58 +0100673 if (session->ti.libssh.channel == channel) {
674 /* first channel requested */
675 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
676 ERRINT;
677 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100678 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100679 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
680 ERR("Session %u: subsystem \"netconf\" requested for the second time.", session->id);
681 return -1;
682 }
683
684 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +0100685 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +0100686 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
687 new_session = calloc(1, sizeof *new_session);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100688 if (!new_session) {
689 ERRMEM;
690 return -1;
691 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100692
693 /* insert the new session */
694 if (!session->ti.libssh.next) {
695 new_session->ti.libssh.next = session;
696 } else {
697 new_session->ti.libssh.next = session->ti.libssh.next;
698 }
699 session->ti.libssh.next = new_session;
700
701 new_session->status = NC_STATUS_STARTING;
702 new_session->side = NC_SERVER;
703 new_session->ti_type = NC_TI_LIBSSH;
704 new_session->ti_lock = session->ti_lock;
705 new_session->ti.libssh.channel = channel;
706 new_session->ti.libssh.session = session->ti.libssh.session;
707 new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
708 new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
709 new_session->port = session->port;
710 new_session->ctx = server_opts.ctx;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100711 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX
712 | (session->flags & NC_SESSION_CALLHOME ? NC_SESSION_CALLHOME : 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100713 }
714
715 return 0;
716}
717
Michal Vasko96164bf2016-01-21 15:41:58 +0100718int
Michal Vaskob48aa812016-01-18 14:13:09 +0100719nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +0100720{
721 const char *str_type, *str_subtype = NULL, *username;
722 int subtype, type;
723 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +0100724
725 type = ssh_message_type(msg);
726 subtype = ssh_message_subtype(msg);
727
728 switch (type) {
729 case SSH_REQUEST_AUTH:
730 str_type = "request-auth";
731 switch (subtype) {
732 case SSH_AUTH_METHOD_NONE:
733 str_subtype = "none";
734 break;
735 case SSH_AUTH_METHOD_PASSWORD:
736 str_subtype = "password";
737 break;
738 case SSH_AUTH_METHOD_PUBLICKEY:
739 str_subtype = "publickey";
740 break;
741 case SSH_AUTH_METHOD_HOSTBASED:
742 str_subtype = "hostbased";
743 break;
744 case SSH_AUTH_METHOD_INTERACTIVE:
745 str_subtype = "interactive";
746 break;
747 case SSH_AUTH_METHOD_GSSAPI_MIC:
748 str_subtype = "gssapi-mic";
749 break;
750 }
751 break;
752
753 case SSH_REQUEST_CHANNEL_OPEN:
754 str_type = "request-channel-open";
755 switch (subtype) {
756 case SSH_CHANNEL_SESSION:
757 str_subtype = "session";
758 break;
759 case SSH_CHANNEL_DIRECT_TCPIP:
760 str_subtype = "direct-tcpip";
761 break;
762 case SSH_CHANNEL_FORWARDED_TCPIP:
763 str_subtype = "forwarded-tcpip";
764 break;
765 case (int)SSH_CHANNEL_X11:
766 str_subtype = "channel-x11";
767 break;
768 case SSH_CHANNEL_UNKNOWN:
769 /* fallthrough */
770 default:
771 str_subtype = "unknown";
772 break;
773 }
774 break;
775
776 case SSH_REQUEST_CHANNEL:
777 str_type = "request-channel";
778 switch (subtype) {
779 case SSH_CHANNEL_REQUEST_PTY:
780 str_subtype = "pty";
781 break;
782 case SSH_CHANNEL_REQUEST_EXEC:
783 str_subtype = "exec";
784 break;
785 case SSH_CHANNEL_REQUEST_SHELL:
786 str_subtype = "shell";
787 break;
788 case SSH_CHANNEL_REQUEST_ENV:
789 str_subtype = "env";
790 break;
791 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
792 str_subtype = "subsystem";
793 break;
794 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
795 str_subtype = "window-change";
796 break;
797 case SSH_CHANNEL_REQUEST_X11:
798 str_subtype = "x11";
799 break;
800 case SSH_CHANNEL_REQUEST_UNKNOWN:
801 /* fallthrough */
802 default:
803 str_subtype = "unknown";
804 break;
805 }
806 break;
807
808 case SSH_REQUEST_SERVICE:
809 str_type = "request-service";
810 str_subtype = ssh_message_service_service(msg);
811 break;
812
813 case SSH_REQUEST_GLOBAL:
814 str_type = "request-global";
815 switch (subtype) {
816 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
817 str_subtype = "tcpip-forward";
818 break;
819 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
820 str_subtype = "cancel-tcpip-forward";
821 break;
822 case SSH_GLOBAL_REQUEST_UNKNOWN:
823 /* fallthrough */
824 default:
825 str_subtype = "unknown";
826 break;
827 }
828 break;
829
830 default:
831 str_type = "unknown";
832 str_subtype = "unknown";
833 break;
834 }
835
836 VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vaskoce319162016-02-03 15:33:08 +0100837 if ((session->status == NC_STATUS_CLOSING) || (session->status == NC_STATUS_INVALID)) {
838 /* "valid" situation if, for example, receiving some auth or channel request timeouted,
839 * but we got it now, during session free */
840 VRB("SSH message arrived on a %s session, the request will be denied.",
841 (session->status == NC_STATUS_CLOSING ? "closing" : "invalid"));
842 ssh_message_reply_default(msg);
843 return 0;
844 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100845 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +0100846
847 /*
848 * process known messages
849 */
850 if (type == SSH_REQUEST_AUTH) {
851 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
852 ERR("User \"%s\" authenticated, but requested another authentication.", session->username);
853 ssh_message_reply_default(msg);
854 return 0;
855 }
856
Michal Vasko2cc4c682016-03-01 09:16:48 +0100857 if (session->ssh_auth_attempts >= ((struct nc_server_ssh_opts *)session->data)->auth_attempts) {
Michal Vasko086311b2016-01-08 09:53:11 +0100858 /* too many failed attempts */
859 ssh_message_reply_default(msg);
860 return 0;
861 }
862
863 /* save the username, do not let the client change it */
864 username = ssh_message_auth_user(msg);
865 if (!session->username) {
866 if (!username) {
867 ERR("Denying an auth request without a username.");
868 return 1;
869 }
870
Michal Vasko05ba9df2016-01-13 14:40:27 +0100871 session->username = lydict_insert(server_opts.ctx, username, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100872 } else if (username) {
873 if (strcmp(username, session->username)) {
874 ERR("User \"%s\" changed its username to \"%s\".", session->username, username);
875 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +0100876 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +0100877 return 1;
878 }
879 }
880
881 if (subtype == SSH_AUTH_METHOD_NONE) {
882 /* libssh will return the supported auth methods */
883 return 1;
884 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
885 nc_sshcb_auth_password(session, msg);
886 return 0;
887 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
888 nc_sshcb_auth_pubkey(session, msg);
889 return 0;
890 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
891 nc_sshcb_auth_kbdint(session, msg);
892 return 0;
893 }
894 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +0100895 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100896 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100897 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100898 }
Michal Vasko086311b2016-01-08 09:53:11 +0100899 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +0100900
Michal Vasko0df67562016-01-21 15:50:11 +0100901 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100902 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
903 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +0100904 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +0100905 } else {
906 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100907 }
908 return 0;
909 }
910 }
911
912 /* we did not process it */
913 return 1;
914}
915
Michal Vasko1a38c862016-01-15 15:50:07 +0100916/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +0100917static int
918nc_open_netconf_channel(struct nc_session *session, int timeout)
919{
Michal Vasko105bf272016-02-03 15:34:35 +0100920 int elapsed_usec = 0, ret, elapsed;
Michal Vasko086311b2016-01-08 09:53:11 +0100921
922 /* message callback is executed twice to give chance for the channel to be
923 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100924 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +0100925 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100926 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +0100927 return -1;
928 }
929
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100930 ret = nc_timedlock(session->ti_lock, timeout, NULL);
931 if (ret != 1) {
932 return ret;
933 }
934
935 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
936 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100937 ERR("Failed to receive SSH messages on a session (%s).",
938 ssh_get_error(session->ti.libssh.session));
Michal Vasko11d142a2016-01-19 15:58:24 +0100939 pthread_mutex_unlock(session->ti_lock);
Michal Vasko086311b2016-01-08 09:53:11 +0100940 return -1;
941 }
942
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100943 if (!session->ti.libssh.channel) {
944 /* we did not receive channel-open, timeout */
945 pthread_mutex_unlock(session->ti_lock);
946 return 0;
947 }
948
949 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
950 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100951 ERR("Failed to receive SSH messages on a session (%s).",
952 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100953 pthread_mutex_unlock(session->ti_lock);
954 return -1;
955 }
956 pthread_mutex_unlock(session->ti_lock);
957
958 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
959 /* we did not receive subsystem-request, timeout */
960 return 0;
961 }
962
963 return 1;
964 }
965
966 while (1) {
967 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100968 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100969 return -1;
970 }
971
Michal Vasko105bf272016-02-03 15:34:35 +0100972 elapsed = 0;
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100973 ret = nc_timedlock(session->ti_lock, timeout, &elapsed);
974 if (ret != 1) {
975 return ret;
976 }
Michal Vasko105bf272016-02-03 15:34:35 +0100977 elapsed_usec += elapsed * 1000;
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100978
979 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
980 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100981 ERR("Failed to receive SSH messages on a session (%s).",
982 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100983 pthread_mutex_unlock(session->ti_lock);
984 return -1;
985 }
986
987 pthread_mutex_unlock(session->ti_lock);
988
Michal Vasko086311b2016-01-08 09:53:11 +0100989 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100990 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +0100991 }
992
Michal Vasko105bf272016-02-03 15:34:35 +0100993 if ((timeout != -1) && (elapsed_usec / 1000 >= timeout)) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100994 /* timeout */
Michal Vasko842522c2016-03-01 09:20:13 +0100995 ERR("Failed to start \"netconf\" SSH subsystem for too long, disconnecting.");
Michal Vasko086311b2016-01-08 09:53:11 +0100996 break;
997 }
998
Michal Vasko086311b2016-01-08 09:53:11 +0100999 usleep(NC_TIMEOUT_STEP);
Michal Vasko105bf272016-02-03 15:34:35 +01001000 elapsed_usec += NC_TIMEOUT_STEP;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001001 }
Michal Vasko086311b2016-01-08 09:53:11 +01001002
Michal Vasko1a38c862016-01-15 15:50:07 +01001003 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001004}
1005
Michal Vasko96164bf2016-01-21 15:41:58 +01001006/* ret 0 - timeout, 1 channel has data, 2 some other channel has data,
1007 * 3 status change, 4 new SSH message, 5 new NETCONF SSH channel, -1 error */
1008int
1009nc_ssh_pollin(struct nc_session *session, int *timeout)
1010{
1011 int ret, elapsed = 0;
1012 struct nc_session *new;
1013
1014 ret = nc_timedlock(session->ti_lock, *timeout, &elapsed);
1015 if (*timeout > 0) {
1016 *timeout -= elapsed;
1017 }
1018
1019 if (ret != 1) {
1020 return ret;
1021 }
1022
1023 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1024 pthread_mutex_unlock(session->ti_lock);
1025
1026 if (ret != SSH_OK) {
1027 ERR("Session %u: failed to receive SSH messages (%s).", session->id,
1028 ssh_get_error(session->ti.libssh.session));
1029 session->status = NC_STATUS_INVALID;
1030 session->term_reason = NC_SESSION_TERM_OTHER;
1031 return 3;
1032 }
1033
1034 /* new SSH message */
1035 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1036 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1037 if (session->ti.libssh.next) {
1038 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1039 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1040 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1041 /* new NETCONF SSH channel */
1042 return 5;
1043 }
1044 }
1045 }
1046
1047 /* just some SSH message */
1048 return 4;
1049 }
1050
1051 /* no new SSH message, maybe NETCONF data? */
1052 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
1053 /* not this one */
1054 if (!ret) {
1055 return 2;
1056 } else if (ret == SSH_ERROR) {
1057 ERR("Session %u: SSH channel error (%s).", session->id,
1058 ssh_get_error(session->ti.libssh.session));
1059 session->status = NC_STATUS_INVALID;
1060 session->term_reason = NC_SESSION_TERM_OTHER;
1061 return 3;
1062 } else if (ret == SSH_EOF) {
1063 ERR("Session %u: communication channel unexpectedly closed (libssh).",
1064 session->id);
1065 session->status = NC_STATUS_INVALID;
1066 session->term_reason = NC_SESSION_TERM_DROPPED;
1067 return 3;
1068 }
1069
1070 return 1;
1071}
1072
Michal Vasko3031aae2016-01-27 16:07:18 +01001073API int
Michal Vasko8f5270d2016-02-29 16:22:25 +01001074nc_connect_callhome_ssh(const char *host, uint16_t port, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001075{
Michal Vasko8f5270d2016-02-29 16:22:25 +01001076 return nc_connect_callhome(host, port, NC_TI_LIBSSH, session);
Michal Vasko3031aae2016-01-27 16:07:18 +01001077}
1078
1079int
Michal Vasko0190bc32016-03-02 15:47:49 +01001080nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko3031aae2016-01-27 16:07:18 +01001081{
1082 struct nc_server_ssh_opts *opts;
Michal Vasko72387da2016-02-02 15:52:41 +01001083 int libssh_auth_methods = 0, elapsed_usec = 0, ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001084
Michal Vasko2cc4c682016-03-01 09:16:48 +01001085 opts = session->data;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001086
Michal Vasko086311b2016-01-08 09:53:11 +01001087 /* other transport-specific data */
1088 session->ti_type = NC_TI_LIBSSH;
1089 session->ti.libssh.session = ssh_new();
1090 if (!session->ti.libssh.session) {
Michal Vaskod083db62016-01-19 10:31:29 +01001091 ERR("Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001092 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001093 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001094 }
1095
Michal Vaskoc61c4492016-01-25 11:13:34 +01001096 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001097 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1098 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001099 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001100 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1101 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001102 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001103 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1104 }
1105 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1106
1107 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001108 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001109 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001110
Michal Vaskoc61c4492016-01-25 11:13:34 +01001111 if (ssh_bind_accept_fd(opts->sshbind, session->ti.libssh.session, sock) == SSH_ERROR) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001112 ERR("SSH failed to accept a new connection (%s).", ssh_get_error(opts->sshbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001113 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001114 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001115 }
1116
Michal Vasko0190bc32016-03-02 15:47:49 +01001117 ssh_set_blocking(session->ti.libssh.session, 0);
1118
1119 while ((ret = ssh_handle_key_exchange(session->ti.libssh.session)) == SSH_AGAIN) {
1120 usleep(NC_TIMEOUT_STEP);
1121 elapsed_usec += NC_TIMEOUT_STEP;
1122 if ((timeout > -1) && (elapsed_usec / 1000 >= timeout)) {
1123 break;
1124 }
1125 }
1126 if (ret == SSH_AGAIN) {
1127 ERR("SSH key exchange timeout.");
1128 return 0;
1129 } else if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001130 ERR("SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001131 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001132 }
1133
1134 /* authenticate */
Michal Vasko0190bc32016-03-02 15:47:49 +01001135 elapsed_usec = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001136 do {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001137 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001138 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001139 return -1;
1140 }
1141
Michal Vasko086311b2016-01-08 09:53:11 +01001142 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001143 ERR("Failed to receive SSH messages on a session (%s).",
1144 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001145 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001146 }
1147
1148 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1149 break;
1150 }
1151
1152 usleep(NC_TIMEOUT_STEP);
Michal Vasko72387da2016-02-02 15:52:41 +01001153 elapsed_usec += NC_TIMEOUT_STEP;
Michal Vaskoab639942016-02-29 16:23:47 +01001154 } while (!opts->auth_timeout || (elapsed_usec / 1000000 < opts->auth_timeout));
Michal Vasko086311b2016-01-08 09:53:11 +01001155
1156 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1157 /* timeout */
Michal Vasko842522c2016-03-01 09:20:13 +01001158 ERR("Client failed to authenticate for too long, disconnecting.");
Michal Vasko1a38c862016-01-15 15:50:07 +01001159 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001160 }
1161
Michal Vasko086311b2016-01-08 09:53:11 +01001162 /* open channel */
Michal Vaskoab639942016-02-29 16:23:47 +01001163 ret = nc_open_netconf_channel(session, opts->auth_timeout ? (opts->auth_timeout * 1000 - elapsed_usec / 1000) : -1);
Michal Vasko1a38c862016-01-15 15:50:07 +01001164 if (ret < 1) {
1165 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001166 }
1167
Michal Vasko96164bf2016-01-21 15:41:58 +01001168 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1169
Michal Vasko1a38c862016-01-15 15:50:07 +01001170 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001171}
1172
Michal Vasko96164bf2016-01-21 15:41:58 +01001173API int
1174nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001175{
Michal Vasko96164bf2016-01-21 15:41:58 +01001176 struct nc_session *new_session = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001177 uint16_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001178
Michal Vasko96164bf2016-01-21 15:41:58 +01001179 if (!ps || !session) {
1180 ERRARG;
1181 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001182 }
1183
Michal Vasko48a63ed2016-03-01 09:48:21 +01001184 /* LOCK */
1185 pthread_mutex_lock(&ps->lock);
1186
Michal Vasko96164bf2016-01-21 15:41:58 +01001187 for (i = 0; i < ps->session_count; ++i) {
1188 if ((ps->sessions[i]->status == NC_STATUS_RUNNING) && (ps->sessions[i]->ti_type == NC_TI_LIBSSH)
1189 && ps->sessions[i]->ti.libssh.next) {
1190 /* an SSH session with more channels */
1191 for (new_session = ps->sessions[i]->ti.libssh.next;
1192 new_session != ps->sessions[i];
1193 new_session = new_session->ti.libssh.next) {
1194 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1195 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1196 /* we found our session */
1197 break;
1198 }
1199 }
1200 if (new_session != ps->sessions[i]) {
1201 break;
1202 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001203
Michal Vasko96164bf2016-01-21 15:41:58 +01001204 new_session = NULL;
1205 }
1206 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001207
Michal Vasko48a63ed2016-03-01 09:48:21 +01001208 /* UNLOCK */
1209 pthread_mutex_unlock(&ps->lock);
1210
Michal Vasko96164bf2016-01-21 15:41:58 +01001211 if (!new_session) {
1212 ERR("No session with a NETCONF SSH channel ready was found.");
1213 return -1;
1214 }
1215
1216 /* assign new SID atomically */
1217 pthread_spin_lock(&server_opts.sid_lock);
1218 new_session->id = server_opts.new_session_id++;
1219 pthread_spin_unlock(&server_opts.sid_lock);
Michal Vaskofb89d772016-01-08 12:25:35 +01001220
Michal Vasko086311b2016-01-08 09:53:11 +01001221 /* NETCONF handshake */
1222 if (nc_handshake(new_session)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001223 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001224 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001225
Michal Vasko086311b2016-01-08 09:53:11 +01001226 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001227 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001228
Michal Vasko96164bf2016-01-21 15:41:58 +01001229 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001230}