blob: 543fdf51555392157ef4af11d0764b2f6e5e6512 [file] [log] [blame]
Michal Vasko086311b2016-01-08 09:53:11 +01001/**
2 * \file session_server_ssh.c
3 * \author Michal Vasko <mvasko@cesnet.cz>
4 * \brief libnetconf2 SSH server session manipulation functions
5 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko086311b2016-01-08 09:53:11 +010013 */
14
15#define _GNU_SOURCE
16
17#include <stdlib.h>
18#include <string.h>
19#include <sys/types.h>
20#include <pwd.h>
21#include <shadow.h>
22#include <crypt.h>
23#include <errno.h>
24
Michal Vasko11d142a2016-01-19 15:58:24 +010025#include "session_server.h"
Michal Vaskoe22c6732016-01-29 11:03:02 +010026#include "session_server_ch.h"
27#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010028
Michal Vasko3031aae2016-01-27 16:07:18 +010029struct nc_server_ssh_opts ssh_ch_opts = {
30 .auth_methods = NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE,
31 .auth_attempts = 3,
32 .auth_timeout = 10
33};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010034pthread_mutex_t ssh_ch_opts_lock = PTHREAD_MUTEX_INITIALIZER;
Michal Vasko086311b2016-01-08 09:53:11 +010035extern struct nc_server_opts server_opts;
Michal Vaskob05053d2016-01-22 16:12:06 +010036
Michal Vasko3031aae2016-01-27 16:07:18 +010037API int
38nc_server_ssh_add_endpt_listen(const char *name, const char *address, uint16_t port)
39{
40 return nc_server_add_endpt_listen(name, address, port, NC_TI_LIBSSH);
41}
Michal Vasko086311b2016-01-08 09:53:11 +010042
Michal Vasko3031aae2016-01-27 16:07:18 +010043API int
Michal Vaskoda514772016-02-01 11:32:01 +010044nc_server_ssh_endpt_set_address(const char *endpt_name, const char *address)
45{
46 return nc_server_endpt_set_address_port(endpt_name, address, 0, NC_TI_LIBSSH);
47}
48
49API int
50nc_server_ssh_endpt_set_port(const char *endpt_name, uint16_t port)
51{
52 return nc_server_endpt_set_address_port(endpt_name, NULL, port, NC_TI_LIBSSH);
53}
54
55API int
Michal Vasko3031aae2016-01-27 16:07:18 +010056nc_server_ssh_del_endpt(const char *name)
57{
58 return nc_server_del_endpt(name, NC_TI_LIBSSH);
59}
Michal Vaskob05053d2016-01-22 16:12:06 +010060
61static int
Michal Vasko3031aae2016-01-27 16:07:18 +010062nc_server_ssh_set_hostkey(const char *privkey_path, struct nc_server_ssh_opts *opts)
Michal Vasko086311b2016-01-08 09:53:11 +010063{
Michal Vasko1a38c862016-01-15 15:50:07 +010064 if (!privkey_path) {
Michal Vasko45e53ae2016-04-07 11:46:03 +020065 ERRARG("privkey_path");
Michal Vasko086311b2016-01-08 09:53:11 +010066 return -1;
67 }
68
Michal Vaskob05053d2016-01-22 16:12:06 +010069 if (!opts->sshbind) {
70 opts->sshbind = ssh_bind_new();
71 if (!opts->sshbind) {
Michal Vaskod083db62016-01-19 10:31:29 +010072 ERR("Failed to create a new ssh_bind.");
Michal Vasko5fcc7142016-02-02 12:21:10 +010073 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +010074 }
75 }
76
Michal Vaskob05053d2016-01-22 16:12:06 +010077 if (ssh_bind_options_set(opts->sshbind, SSH_BIND_OPTIONS_HOSTKEY, privkey_path) != SSH_OK) {
Michal Vaskoc61c4492016-01-25 11:13:34 +010078 if (eaccess(privkey_path, R_OK)) {
79 ERR("Failed to set host key (%s).", strerror(errno));
80 } else {
81 ERR("Failed to set host key (%s).", ssh_get_error(opts->sshbind));
82 }
Michal Vasko5fcc7142016-02-02 12:21:10 +010083 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +010084 }
Michal Vaskod45e25a2016-01-08 15:48:44 +010085
Michal Vasko5fcc7142016-02-02 12:21:10 +010086 return 0;
Michal Vaskob05053d2016-01-22 16:12:06 +010087}
88
89API int
Michal Vasko3031aae2016-01-27 16:07:18 +010090nc_server_ssh_endpt_set_hostkey(const char *endpt_name, const char *privkey_path)
Michal Vaskob05053d2016-01-22 16:12:06 +010091{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010092 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +010093 struct nc_endpt *endpt;
94
Michal Vasko51e514d2016-02-02 15:51:52 +010095 /* LOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010096 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +010097 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +010098 return -1;
99 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100100 ret = nc_server_ssh_set_hostkey(privkey_path, endpt->ti_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100101 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100102 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100103
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100104 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100105}
106
107API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100108nc_server_ssh_ch_set_hostkey(const char *privkey_path)
Michal Vaskob05053d2016-01-22 16:12:06 +0100109{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100110 int ret;
111
112 /* OPTS LOCK */
113 pthread_mutex_lock(&ssh_ch_opts_lock);
114 ret = nc_server_ssh_set_hostkey(privkey_path, &ssh_ch_opts);
115 /* OPTS UNLOCK */
116 pthread_mutex_unlock(&ssh_ch_opts_lock);
117
118 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100119}
120
121static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100122nc_server_ssh_set_banner(const char *banner, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100123{
Michal Vaskob05053d2016-01-22 16:12:06 +0100124 if (!banner) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200125 ERRARG("banner");
Michal Vaskob05053d2016-01-22 16:12:06 +0100126 return -1;
127 }
128
Michal Vaskob05053d2016-01-22 16:12:06 +0100129 if (!opts->sshbind) {
130 opts->sshbind = ssh_bind_new();
131 if (!opts->sshbind) {
132 ERR("Failed to create a new ssh_bind.");
Michal Vasko5fcc7142016-02-02 12:21:10 +0100133 return -1;
Michal Vaskob05053d2016-01-22 16:12:06 +0100134 }
135 }
136
137 ssh_bind_options_set(opts->sshbind, SSH_BIND_OPTIONS_BANNER, banner);
138
Michal Vaskob05053d2016-01-22 16:12:06 +0100139 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100140}
141
142API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100143nc_server_ssh_endpt_set_banner(const char *endpt_name, const char *banner)
Michal Vasko086311b2016-01-08 09:53:11 +0100144{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100145 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100146 struct nc_endpt *endpt;
147
Michal Vasko51e514d2016-02-02 15:51:52 +0100148 /* LOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100149 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100150 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100151 return -1;
152 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100153 ret = nc_server_ssh_set_banner(banner, endpt->ti_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100154 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100155 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100156
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100157 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100158}
159
160API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100161nc_server_ssh_ch_set_banner(const char *banner)
Michal Vasko086311b2016-01-08 09:53:11 +0100162{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100163 int ret;
164
165 /* OPTS LOCK */
166 pthread_mutex_lock(&ssh_ch_opts_lock);
167 ret = nc_server_ssh_set_banner(banner, &ssh_ch_opts);
168 /* OPTS UNLOCK */
169 pthread_mutex_unlock(&ssh_ch_opts_lock);
170
171 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100172}
173
174static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100175nc_server_ssh_set_auth_methods(int auth_methods, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100176{
Michal Vasko086311b2016-01-08 09:53:11 +0100177 if (!(auth_methods & NC_SSH_AUTH_PUBLICKEY) && !(auth_methods & NC_SSH_AUTH_PASSWORD)
178 && !(auth_methods & NC_SSH_AUTH_INTERACTIVE)) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200179 ERRARG("auth_methods");
Michal Vasko086311b2016-01-08 09:53:11 +0100180 return -1;
181 }
182
Michal Vaskob05053d2016-01-22 16:12:06 +0100183 opts->auth_methods = auth_methods;
184 return 0;
185}
186
187API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100188nc_server_ssh_endpt_set_auth_methods(const char *endpt_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100189{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100190 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100191 struct nc_endpt *endpt;
192
Michal Vasko51e514d2016-02-02 15:51:52 +0100193 /* LOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100194 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100195 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100196 return -1;
197 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100198 ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->ti_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100199 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100200 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100201
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100202 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100203}
204
205API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100206nc_server_ssh_ch_set_auth_methods(int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100207{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100208 int ret;
209
210 /* OPTS LOCK */
211 pthread_mutex_lock(&ssh_ch_opts_lock);
212 ret = nc_server_ssh_set_auth_methods(auth_methods, &ssh_ch_opts);
213 /* OPTS UNLOCK */
214 pthread_mutex_unlock(&ssh_ch_opts_lock);
215
216 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100217}
218
219static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100220nc_server_ssh_set_auth_attempts(uint16_t auth_attempts, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100221{
Michal Vaskob05053d2016-01-22 16:12:06 +0100222 if (!auth_attempts) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200223 ERRARG("auth_attempts");
Michal Vaskob05053d2016-01-22 16:12:06 +0100224 return -1;
225 }
226
Michal Vaskob05053d2016-01-22 16:12:06 +0100227 opts->auth_attempts = auth_attempts;
Michal Vasko086311b2016-01-08 09:53:11 +0100228 return 0;
229}
230
231API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100232nc_server_ssh_endpt_set_auth_attempts(const char *endpt_name, uint16_t auth_attempts)
Michal Vasko086311b2016-01-08 09:53:11 +0100233{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100234 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100235 struct nc_endpt *endpt;
236
Michal Vasko51e514d2016-02-02 15:51:52 +0100237 /* LOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100238 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100239 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100240 return -1;
241 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100242 ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->ti_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100243 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100244 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100245
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100246 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100247}
248
249API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100250nc_server_ssh_set_ch_auth_attempts(uint16_t auth_attempts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100251{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100252 int ret;
253
254 /* OPTS LOCK */
255 pthread_mutex_lock(&ssh_ch_opts_lock);
256 ret = nc_server_ssh_set_auth_attempts(auth_attempts, &ssh_ch_opts);
257 /* OPTS UNLOCK */
258 pthread_mutex_unlock(&ssh_ch_opts_lock);
259
260 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100261}
262
263static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100264nc_server_ssh_set_auth_timeout(uint16_t auth_timeout, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100265{
Michal Vaskob05053d2016-01-22 16:12:06 +0100266 if (!auth_timeout) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200267 ERRARG("auth_timeout");
Michal Vasko086311b2016-01-08 09:53:11 +0100268 return -1;
269 }
270
Michal Vaskob05053d2016-01-22 16:12:06 +0100271 opts->auth_timeout = auth_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100272 return 0;
273}
274
275API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100276nc_server_ssh_endpt_set_auth_timeout(const char *endpt_name, uint16_t auth_timeout)
Michal Vasko086311b2016-01-08 09:53:11 +0100277{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100278 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100279 struct nc_endpt *endpt;
280
Michal Vasko51e514d2016-02-02 15:51:52 +0100281 /* LOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100282 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100283 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100284 return -1;
285 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100286 ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->ti_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100287 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100288 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100289
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100290 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100291}
292
293API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100294nc_server_ssh_ch_set_auth_timeout(uint16_t auth_timeout)
Michal Vaskob05053d2016-01-22 16:12:06 +0100295{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100296 int ret;
297
298 /* OPTS LOCK */
299 pthread_mutex_lock(&ssh_ch_opts_lock);
300 ret = nc_server_ssh_set_auth_timeout(auth_timeout, &ssh_ch_opts);
301 /* OPTS UNLOCK */
302 pthread_mutex_unlock(&ssh_ch_opts_lock);
303
304 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100305}
306
307static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100308nc_server_ssh_add_authkey(const char *pubkey_path, const char *username, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100309{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200310 if (!pubkey_path) {
311 ERRARG("pubkey_path");
312 return -1;
313 } else if (!username) {
314 ERRARG("username");
Michal Vasko086311b2016-01-08 09:53:11 +0100315 return -1;
316 }
317
Michal Vaskob05053d2016-01-22 16:12:06 +0100318 ++opts->authkey_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100319 opts->authkeys = nc_realloc(opts->authkeys, opts->authkey_count * sizeof *opts->authkeys);
320 if (!opts->authkeys) {
321 ERRMEM;
322 return -1;
323 }
Michal Vaskob05053d2016-01-22 16:12:06 +0100324 opts->authkeys[opts->authkey_count - 1].path = lydict_insert(server_opts.ctx, pubkey_path, 0);
325 opts->authkeys[opts->authkey_count - 1].username = lydict_insert(server_opts.ctx, username, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +0100326
Michal Vasko086311b2016-01-08 09:53:11 +0100327 return 0;
328}
329
330API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100331nc_server_ssh_endpt_add_authkey(const char *endpt_name, const char *pubkey_path, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100332{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100333 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100334 struct nc_endpt *endpt;
335
Michal Vasko51e514d2016-02-02 15:51:52 +0100336 /* LOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100337 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100338 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100339 return -1;
340 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100341 ret = nc_server_ssh_add_authkey(pubkey_path, username, endpt->ti_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100342 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100343 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100344
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100345 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100346}
347
348API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100349nc_server_ssh_ch_add_authkey(const char *pubkey_path, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100350{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100351 int ret;
352
353 /* OPTS LOCK */
354 pthread_mutex_lock(&ssh_ch_opts_lock);
355 ret = nc_server_ssh_add_authkey(pubkey_path, username, &ssh_ch_opts);
356 /* OPTS UNLOCK */
357 pthread_mutex_unlock(&ssh_ch_opts_lock);
358
359 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100360}
361
362static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100363nc_server_ssh_del_authkey(const char *pubkey_path, const char *username, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100364{
Michal Vasko086311b2016-01-08 09:53:11 +0100365 uint32_t i;
366 int ret = -1;
367
Michal Vasko1a38c862016-01-15 15:50:07 +0100368 if (!pubkey_path && !username) {
Michal Vaskob05053d2016-01-22 16:12:06 +0100369 for (i = 0; i < opts->authkey_count; ++i) {
370 lydict_remove(server_opts.ctx, opts->authkeys[i].path);
371 lydict_remove(server_opts.ctx, opts->authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100372
Michal Vasko086311b2016-01-08 09:53:11 +0100373 ret = 0;
374 }
Michal Vaskob05053d2016-01-22 16:12:06 +0100375 free(opts->authkeys);
376 opts->authkeys = NULL;
377 opts->authkey_count = 0;
Michal Vasko1a38c862016-01-15 15:50:07 +0100378 } else {
Michal Vaskob05053d2016-01-22 16:12:06 +0100379 for (i = 0; i < opts->authkey_count; ++i) {
380 if ((!pubkey_path || !strcmp(opts->authkeys[i].path, pubkey_path))
381 && (!username || !strcmp(opts->authkeys[i].username, username))) {
Michal Vaskob05053d2016-01-22 16:12:06 +0100382 lydict_remove(server_opts.ctx, opts->authkeys[i].path);
383 lydict_remove(server_opts.ctx, opts->authkeys[i].username);
Michal Vasko1a38c862016-01-15 15:50:07 +0100384
Michal Vaskob05053d2016-01-22 16:12:06 +0100385 --opts->authkey_count;
Michal Vaskoc0256492016-02-02 12:19:06 +0100386 if (i < opts->authkey_count) {
387 memcpy(&opts->authkeys[i], &opts->authkeys[opts->authkey_count], sizeof *opts->authkeys);
388 } else if (!opts->authkey_count) {
389 free(opts->authkeys);
390 opts->authkeys = NULL;
391 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100392
393 ret = 0;
394 }
395 }
Michal Vasko086311b2016-01-08 09:53:11 +0100396 }
397
398 return ret;
399}
400
Michal Vaskob05053d2016-01-22 16:12:06 +0100401API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100402nc_server_ssh_endpt_del_authkey(const char *endpt_name, const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100403{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100404 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100405 struct nc_endpt *endpt;
406
Michal Vasko51e514d2016-02-02 15:51:52 +0100407 /* LOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100408 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100409 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100410 return -1;
411 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100412 ret = nc_server_ssh_del_authkey(pubkey_path, username, endpt->ti_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100413 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100414 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100415
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100416 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100417}
418
419API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100420nc_server_ssh_ch_del_authkey(const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100421{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100422 int ret;
423
424 /* OPTS LOCK */
425 pthread_mutex_lock(&ssh_ch_opts_lock);
426 ret = nc_server_ssh_del_authkey(pubkey_path, username, &ssh_ch_opts);
427 /* OPTS UNLOCK */
428 pthread_mutex_unlock(&ssh_ch_opts_lock);
429
430 return ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100431}
432
433void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100434nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100435{
436 if (opts->sshbind) {
437 ssh_bind_free(opts->sshbind);
438 opts->sshbind = NULL;
439 }
440
441 nc_server_ssh_del_authkey(NULL, NULL, opts);
Michal Vaskob05053d2016-01-22 16:12:06 +0100442}
443
Michal Vasko086311b2016-01-08 09:53:11 +0100444API void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100445nc_server_ssh_ch_clear_opts(void)
Michal Vasko086311b2016-01-08 09:53:11 +0100446{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100447 /* OPTS LOCK */
448 pthread_mutex_lock(&ssh_ch_opts_lock);
449 nc_server_ssh_clear_opts(&ssh_ch_opts);
450 /* OPTS UNLOCK */
451 pthread_mutex_unlock(&ssh_ch_opts_lock);
Michal Vasko086311b2016-01-08 09:53:11 +0100452}
453
454static char *
455auth_password_get_pwd_hash(const char *username)
456{
457 struct passwd *pwd, pwd_buf;
458 struct spwd *spwd, spwd_buf;
459 char *pass_hash = NULL, buf[256];
460
461 getpwnam_r(username, &pwd_buf, buf, 256, &pwd);
462 if (!pwd) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100463 VRB("User \"%s\" not found locally.", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100464 return NULL;
465 }
466
467 if (!strcmp(pwd->pw_passwd, "x")) {
468 getspnam_r(username, &spwd_buf, buf, 256, &spwd);
469 if (!spwd) {
470 VRB("Failed to retrieve the shadow entry for \"%s\".", username);
471 return NULL;
472 }
473
474 pass_hash = spwd->sp_pwdp;
475 } else {
476 pass_hash = pwd->pw_passwd;
477 }
478
479 if (!pass_hash) {
Michal Vaskod083db62016-01-19 10:31:29 +0100480 ERR("No password could be retrieved for \"%s\".", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100481 return NULL;
482 }
483
484 /* check the hash structure for special meaning */
485 if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) {
486 VRB("User \"%s\" is not allowed to authenticate using a password.", username);
487 return NULL;
488 }
489 if (!strcmp(pass_hash, "*NP*")) {
490 VRB("Retrieving password for \"%s\" from a NIS+ server not supported.", username);
491 return NULL;
492 }
493
494 return strdup(pass_hash);
495}
496
497static int
498auth_password_compare_pwd(const char *pass_hash, const char *pass_clear)
499{
500 char *new_pass_hash;
501 struct crypt_data cdata;
502
503 if (!pass_hash[0]) {
504 if (!pass_clear[0]) {
505 WRN("User authentication successful with an empty password!");
506 return 0;
507 } else {
508 /* the user did now know he does not need any password,
509 * (which should not be used) so deny authentication */
510 return 1;
511 }
512 }
513
514 cdata.initialized = 0;
515 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
516 return strcmp(new_pass_hash, pass_hash);
517}
518
519static void
520nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
521{
522 char *pass_hash;
523
524 pass_hash = auth_password_get_pwd_hash(session->username);
525 if (pass_hash && !auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100526 VRB("User \"%s\" authenticated.", session->username);
Michal Vasko086311b2016-01-08 09:53:11 +0100527 ssh_message_auth_reply_success(msg, 0);
528 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
529 free(pass_hash);
530 return;
531 }
532
533 free(pass_hash);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100534 ++session->ssh_auth_attempts;
Michal Vasko296fee82016-05-04 08:57:31 +0200535 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100536 ssh_message_reply_default(msg);
537}
538
539static void
540nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
541{
542 char *pass_hash;
543
544 if (!ssh_message_auth_kbdint_is_response(msg)) {
545 const char *prompts[] = {"Password: "};
546 char echo[] = {0};
547
548 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
549 } else {
550 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {
551 ssh_message_reply_default(msg);
552 return;
553 }
554 pass_hash = auth_password_get_pwd_hash(session->username);
555 if (!pass_hash) {
556 ssh_message_reply_default(msg);
557 return;
558 }
559 if (!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0))) {
560 VRB("User \"%s\" authenticated.", session->username);
561 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
562 ssh_message_auth_reply_success(msg, 0);
563 } else {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100564 ++session->ssh_auth_attempts;
565 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100566 ssh_message_reply_default(msg);
567 }
Radek Krejcifb533742016-03-04 15:12:54 +0100568 free(pass_hash);
Michal Vasko086311b2016-01-08 09:53:11 +0100569 }
570}
571
572static const char *
Michal Vasko3031aae2016-01-27 16:07:18 +0100573auth_pubkey_compare_key(struct nc_server_ssh_opts *opts, ssh_key key)
Michal Vasko086311b2016-01-08 09:53:11 +0100574{
575 uint32_t i;
576 ssh_key pub_key;
Michal Vasko76e3a352016-01-18 09:07:00 +0100577 const char *username = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100578
Michal Vasko3031aae2016-01-27 16:07:18 +0100579 for (i = 0; i < opts->authkey_count; ++i) {
580 if (ssh_pki_import_pubkey_file(opts->authkeys[i].path, &pub_key) != SSH_OK) {
581 if (eaccess(opts->authkeys[i].path, R_OK)) {
582 WRN("Failed to import the public key \"%s\" (%s).", opts->authkeys[i].path, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100583 } else {
Michal Vaskoe5612cf2016-02-02 15:52:16 +0100584 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 +0100585 }
586 continue;
587 }
588
589 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
590 ssh_key_free(pub_key);
591 break;
592 }
593
594 ssh_key_free(pub_key);
595 }
596
Michal Vasko3031aae2016-01-27 16:07:18 +0100597 if (i < opts->authkey_count) {
598 username = opts->authkeys[i].username;
Michal Vasko086311b2016-01-08 09:53:11 +0100599 }
600
601 return username;
602}
603
604static void
605nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
606{
607 const char *username;
608 int signature_state;
609
610 signature_state = ssh_message_auth_publickey_state(msg);
611 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
612 VRB("User \"%s\" authenticated.", session->username);
613 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
614 ssh_message_auth_reply_success(msg, 0);
615 return;
616
617 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Michal Vasko2cc4c682016-03-01 09:16:48 +0100618 if ((username = auth_pubkey_compare_key(session->data, ssh_message_auth_pubkey(msg))) == NULL) {
Michal Vasko086311b2016-01-08 09:53:11 +0100619 VRB("User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
620
621 } else if (strcmp(session->username, username)) {
622 VRB("User \"%s\" is not the username identified with the presented public key.", session->username);
623
624 } else {
625 /* accepting only the use of a public key */
626 ssh_message_auth_reply_pk_ok_simple(msg);
627 return;
628 }
629 }
630
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100631 ++session->ssh_auth_attempts;
632 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100633 ssh_message_reply_default(msg);
634}
635
636static int
Michal Vasko96164bf2016-01-21 15:41:58 +0100637nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100638{
Michal Vasko96164bf2016-01-21 15:41:58 +0100639 ssh_channel chan;
640
641 /* first channel request */
642 if (!session->ti.libssh.channel) {
643 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100644 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100645 return -1;
646 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100647 chan = ssh_message_channel_request_open_reply_accept(msg);
648 if (!chan) {
649 ERR("Failed to create a new SSH channel.");
650 return -1;
651 }
652 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +0100653
Michal Vasko96164bf2016-01-21 15:41:58 +0100654 /* additional channel request */
655 } else {
656 chan = ssh_message_channel_request_open_reply_accept(msg);
657 if (!chan) {
658 ERR("Session %u: failed to create a new SSH channel.", session->id);
659 return -1;
660 }
661 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +0100662 }
663
Michal Vasko086311b2016-01-08 09:53:11 +0100664 return 0;
665}
666
667static int
668nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
669{
Michal Vasko96164bf2016-01-21 15:41:58 +0100670 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +0100671
Michal Vasko96164bf2016-01-21 15:41:58 +0100672 if (strcmp(subsystem, "netconf")) {
673 WRN("Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +0100674 return -1;
675 }
676
Michal Vasko96164bf2016-01-21 15:41:58 +0100677 if (session->ti.libssh.channel == channel) {
678 /* first channel requested */
679 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
680 ERRINT;
681 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100682 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100683 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
684 ERR("Session %u: subsystem \"netconf\" requested for the second time.", session->id);
685 return -1;
686 }
687
688 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +0100689 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +0100690 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
691 new_session = calloc(1, sizeof *new_session);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100692 if (!new_session) {
693 ERRMEM;
694 return -1;
695 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100696
697 /* insert the new session */
698 if (!session->ti.libssh.next) {
699 new_session->ti.libssh.next = session;
700 } else {
701 new_session->ti.libssh.next = session->ti.libssh.next;
702 }
703 session->ti.libssh.next = new_session;
704
705 new_session->status = NC_STATUS_STARTING;
706 new_session->side = NC_SERVER;
707 new_session->ti_type = NC_TI_LIBSSH;
708 new_session->ti_lock = session->ti_lock;
709 new_session->ti.libssh.channel = channel;
710 new_session->ti.libssh.session = session->ti.libssh.session;
711 new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
712 new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
713 new_session->port = session->port;
714 new_session->ctx = server_opts.ctx;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100715 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX
716 | (session->flags & NC_SESSION_CALLHOME ? NC_SESSION_CALLHOME : 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100717 }
718
719 return 0;
720}
721
Michal Vasko96164bf2016-01-21 15:41:58 +0100722int
Michal Vaskob48aa812016-01-18 14:13:09 +0100723nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +0100724{
725 const char *str_type, *str_subtype = NULL, *username;
726 int subtype, type;
727 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +0100728
729 type = ssh_message_type(msg);
730 subtype = ssh_message_subtype(msg);
731
732 switch (type) {
733 case SSH_REQUEST_AUTH:
734 str_type = "request-auth";
735 switch (subtype) {
736 case SSH_AUTH_METHOD_NONE:
737 str_subtype = "none";
738 break;
739 case SSH_AUTH_METHOD_PASSWORD:
740 str_subtype = "password";
741 break;
742 case SSH_AUTH_METHOD_PUBLICKEY:
743 str_subtype = "publickey";
744 break;
745 case SSH_AUTH_METHOD_HOSTBASED:
746 str_subtype = "hostbased";
747 break;
748 case SSH_AUTH_METHOD_INTERACTIVE:
749 str_subtype = "interactive";
750 break;
751 case SSH_AUTH_METHOD_GSSAPI_MIC:
752 str_subtype = "gssapi-mic";
753 break;
754 }
755 break;
756
757 case SSH_REQUEST_CHANNEL_OPEN:
758 str_type = "request-channel-open";
759 switch (subtype) {
760 case SSH_CHANNEL_SESSION:
761 str_subtype = "session";
762 break;
763 case SSH_CHANNEL_DIRECT_TCPIP:
764 str_subtype = "direct-tcpip";
765 break;
766 case SSH_CHANNEL_FORWARDED_TCPIP:
767 str_subtype = "forwarded-tcpip";
768 break;
769 case (int)SSH_CHANNEL_X11:
770 str_subtype = "channel-x11";
771 break;
772 case SSH_CHANNEL_UNKNOWN:
773 /* fallthrough */
774 default:
775 str_subtype = "unknown";
776 break;
777 }
778 break;
779
780 case SSH_REQUEST_CHANNEL:
781 str_type = "request-channel";
782 switch (subtype) {
783 case SSH_CHANNEL_REQUEST_PTY:
784 str_subtype = "pty";
785 break;
786 case SSH_CHANNEL_REQUEST_EXEC:
787 str_subtype = "exec";
788 break;
789 case SSH_CHANNEL_REQUEST_SHELL:
790 str_subtype = "shell";
791 break;
792 case SSH_CHANNEL_REQUEST_ENV:
793 str_subtype = "env";
794 break;
795 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
796 str_subtype = "subsystem";
797 break;
798 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
799 str_subtype = "window-change";
800 break;
801 case SSH_CHANNEL_REQUEST_X11:
802 str_subtype = "x11";
803 break;
804 case SSH_CHANNEL_REQUEST_UNKNOWN:
805 /* fallthrough */
806 default:
807 str_subtype = "unknown";
808 break;
809 }
810 break;
811
812 case SSH_REQUEST_SERVICE:
813 str_type = "request-service";
814 str_subtype = ssh_message_service_service(msg);
815 break;
816
817 case SSH_REQUEST_GLOBAL:
818 str_type = "request-global";
819 switch (subtype) {
820 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
821 str_subtype = "tcpip-forward";
822 break;
823 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
824 str_subtype = "cancel-tcpip-forward";
825 break;
826 case SSH_GLOBAL_REQUEST_UNKNOWN:
827 /* fallthrough */
828 default:
829 str_subtype = "unknown";
830 break;
831 }
832 break;
833
834 default:
835 str_type = "unknown";
836 str_subtype = "unknown";
837 break;
838 }
839
840 VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vaskoce319162016-02-03 15:33:08 +0100841 if ((session->status == NC_STATUS_CLOSING) || (session->status == NC_STATUS_INVALID)) {
842 /* "valid" situation if, for example, receiving some auth or channel request timeouted,
843 * but we got it now, during session free */
844 VRB("SSH message arrived on a %s session, the request will be denied.",
845 (session->status == NC_STATUS_CLOSING ? "closing" : "invalid"));
846 ssh_message_reply_default(msg);
847 return 0;
848 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100849 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +0100850
851 /*
852 * process known messages
853 */
854 if (type == SSH_REQUEST_AUTH) {
855 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
856 ERR("User \"%s\" authenticated, but requested another authentication.", session->username);
857 ssh_message_reply_default(msg);
858 return 0;
859 }
860
Michal Vasko2cc4c682016-03-01 09:16:48 +0100861 if (session->ssh_auth_attempts >= ((struct nc_server_ssh_opts *)session->data)->auth_attempts) {
Michal Vasko086311b2016-01-08 09:53:11 +0100862 /* too many failed attempts */
863 ssh_message_reply_default(msg);
864 return 0;
865 }
866
867 /* save the username, do not let the client change it */
868 username = ssh_message_auth_user(msg);
869 if (!session->username) {
870 if (!username) {
871 ERR("Denying an auth request without a username.");
872 return 1;
873 }
874
Michal Vasko05ba9df2016-01-13 14:40:27 +0100875 session->username = lydict_insert(server_opts.ctx, username, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100876 } else if (username) {
877 if (strcmp(username, session->username)) {
878 ERR("User \"%s\" changed its username to \"%s\".", session->username, username);
879 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +0100880 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +0100881 return 1;
882 }
883 }
884
885 if (subtype == SSH_AUTH_METHOD_NONE) {
886 /* libssh will return the supported auth methods */
887 return 1;
888 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
889 nc_sshcb_auth_password(session, msg);
890 return 0;
891 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
892 nc_sshcb_auth_pubkey(session, msg);
893 return 0;
894 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
895 nc_sshcb_auth_kbdint(session, msg);
896 return 0;
897 }
898 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +0100899 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100900 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100901 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100902 }
Michal Vasko086311b2016-01-08 09:53:11 +0100903 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +0100904
Michal Vasko0df67562016-01-21 15:50:11 +0100905 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100906 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
907 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +0100908 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +0100909 } else {
910 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100911 }
912 return 0;
913 }
914 }
915
916 /* we did not process it */
917 return 1;
918}
919
Michal Vasko1a38c862016-01-15 15:50:07 +0100920/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +0100921static int
922nc_open_netconf_channel(struct nc_session *session, int timeout)
923{
Michal Vasko62be1ce2016-03-03 13:24:52 +0100924 int elapsed_usec = 0, ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100925
926 /* message callback is executed twice to give chance for the channel to be
927 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100928 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +0100929 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100930 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +0100931 return -1;
932 }
933
Michal Vasko62be1ce2016-03-03 13:24:52 +0100934 ret = nc_timedlock(session->ti_lock, timeout);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100935 if (ret != 1) {
936 return ret;
937 }
938
939 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
940 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100941 ERR("Failed to receive SSH messages on a session (%s).",
942 ssh_get_error(session->ti.libssh.session));
Michal Vasko11d142a2016-01-19 15:58:24 +0100943 pthread_mutex_unlock(session->ti_lock);
Michal Vasko086311b2016-01-08 09:53:11 +0100944 return -1;
945 }
946
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100947 if (!session->ti.libssh.channel) {
948 /* we did not receive channel-open, timeout */
949 pthread_mutex_unlock(session->ti_lock);
950 return 0;
951 }
952
953 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
954 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100955 ERR("Failed to receive SSH messages on a session (%s).",
956 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100957 pthread_mutex_unlock(session->ti_lock);
958 return -1;
959 }
960 pthread_mutex_unlock(session->ti_lock);
961
962 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
963 /* we did not receive subsystem-request, timeout */
964 return 0;
965 }
966
967 return 1;
968 }
969
970 while (1) {
971 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100972 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100973 return -1;
974 }
975
Michal Vasko62be1ce2016-03-03 13:24:52 +0100976 ret = nc_timedlock(session->ti_lock, timeout);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100977 if (ret != 1) {
978 return ret;
979 }
980
981 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
982 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100983 ERR("Failed to receive SSH messages on a session (%s).",
984 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100985 pthread_mutex_unlock(session->ti_lock);
986 return -1;
987 }
988
989 pthread_mutex_unlock(session->ti_lock);
990
Michal Vasko086311b2016-01-08 09:53:11 +0100991 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100992 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +0100993 }
994
Michal Vasko105bf272016-02-03 15:34:35 +0100995 if ((timeout != -1) && (elapsed_usec / 1000 >= timeout)) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100996 /* timeout */
Michal Vasko842522c2016-03-01 09:20:13 +0100997 ERR("Failed to start \"netconf\" SSH subsystem for too long, disconnecting.");
Michal Vasko086311b2016-01-08 09:53:11 +0100998 break;
999 }
1000
Michal Vasko086311b2016-01-08 09:53:11 +01001001 usleep(NC_TIMEOUT_STEP);
Michal Vasko105bf272016-02-03 15:34:35 +01001002 elapsed_usec += NC_TIMEOUT_STEP;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001003 }
Michal Vasko086311b2016-01-08 09:53:11 +01001004
Michal Vasko1a38c862016-01-15 15:50:07 +01001005 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001006}
1007
Michal Vasko96164bf2016-01-21 15:41:58 +01001008/* ret 0 - timeout, 1 channel has data, 2 some other channel has data,
1009 * 3 status change, 4 new SSH message, 5 new NETCONF SSH channel, -1 error */
1010int
Michal Vasko62be1ce2016-03-03 13:24:52 +01001011nc_ssh_pollin(struct nc_session *session, int timeout)
Michal Vasko96164bf2016-01-21 15:41:58 +01001012{
Michal Vasko62be1ce2016-03-03 13:24:52 +01001013 int ret;
Michal Vasko96164bf2016-01-21 15:41:58 +01001014 struct nc_session *new;
1015
Michal Vasko62be1ce2016-03-03 13:24:52 +01001016 ret = nc_timedlock(session->ti_lock, timeout);
Michal Vasko96164bf2016-01-21 15:41:58 +01001017
1018 if (ret != 1) {
1019 return ret;
1020 }
1021
1022 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1023 pthread_mutex_unlock(session->ti_lock);
1024
1025 if (ret != SSH_OK) {
1026 ERR("Session %u: failed to receive SSH messages (%s).", session->id,
1027 ssh_get_error(session->ti.libssh.session));
1028 session->status = NC_STATUS_INVALID;
1029 session->term_reason = NC_SESSION_TERM_OTHER;
1030 return 3;
1031 }
1032
1033 /* new SSH message */
1034 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1035 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1036 if (session->ti.libssh.next) {
1037 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1038 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1039 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1040 /* new NETCONF SSH channel */
1041 return 5;
1042 }
1043 }
1044 }
1045
1046 /* just some SSH message */
1047 return 4;
1048 }
1049
1050 /* no new SSH message, maybe NETCONF data? */
1051 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
1052 /* not this one */
1053 if (!ret) {
1054 return 2;
1055 } else if (ret == SSH_ERROR) {
1056 ERR("Session %u: SSH channel error (%s).", session->id,
1057 ssh_get_error(session->ti.libssh.session));
1058 session->status = NC_STATUS_INVALID;
1059 session->term_reason = NC_SESSION_TERM_OTHER;
1060 return 3;
1061 } else if (ret == SSH_EOF) {
1062 ERR("Session %u: communication channel unexpectedly closed (libssh).",
1063 session->id);
1064 session->status = NC_STATUS_INVALID;
1065 session->term_reason = NC_SESSION_TERM_DROPPED;
1066 return 3;
1067 }
1068
1069 return 1;
1070}
1071
Michal Vasko3031aae2016-01-27 16:07:18 +01001072API int
Michal Vasko8f5270d2016-02-29 16:22:25 +01001073nc_connect_callhome_ssh(const char *host, uint16_t port, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001074{
Michal Vasko8f5270d2016-02-29 16:22:25 +01001075 return nc_connect_callhome(host, port, NC_TI_LIBSSH, session);
Michal Vasko3031aae2016-01-27 16:07:18 +01001076}
1077
1078int
Michal Vasko0190bc32016-03-02 15:47:49 +01001079nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko3031aae2016-01-27 16:07:18 +01001080{
1081 struct nc_server_ssh_opts *opts;
Michal Vasko72387da2016-02-02 15:52:41 +01001082 int libssh_auth_methods = 0, elapsed_usec = 0, ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001083
Michal Vasko2cc4c682016-03-01 09:16:48 +01001084 opts = session->data;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001085
Michal Vasko086311b2016-01-08 09:53:11 +01001086 /* other transport-specific data */
1087 session->ti_type = NC_TI_LIBSSH;
1088 session->ti.libssh.session = ssh_new();
1089 if (!session->ti.libssh.session) {
Michal Vaskod083db62016-01-19 10:31:29 +01001090 ERR("Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001091 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001092 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001093 }
1094
Michal Vaskoc61c4492016-01-25 11:13:34 +01001095 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001096 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1097 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001098 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001099 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1100 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001101 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001102 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1103 }
1104 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1105
1106 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001107 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001108 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001109
Michal Vaskoc61c4492016-01-25 11:13:34 +01001110 if (ssh_bind_accept_fd(opts->sshbind, session->ti.libssh.session, sock) == SSH_ERROR) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001111 ERR("SSH failed to accept a new connection (%s).", ssh_get_error(opts->sshbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001112 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001113 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001114 }
1115
Michal Vasko0190bc32016-03-02 15:47:49 +01001116 ssh_set_blocking(session->ti.libssh.session, 0);
1117
1118 while ((ret = ssh_handle_key_exchange(session->ti.libssh.session)) == SSH_AGAIN) {
Michal Vaskoe65b4492016-03-03 11:57:51 +01001119 /* this tends to take longer */
1120 usleep(NC_TIMEOUT_STEP * 20);
1121 elapsed_usec += NC_TIMEOUT_STEP * 20;
Michal Vasko0190bc32016-03-02 15:47:49 +01001122 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 Vasko45e53ae2016-04-07 11:46:03 +02001179 if (!ps) {
1180 ERRARG("ps");
1181 return -1;
1182 } else if (!session) {
1183 ERRARG("session");
Michal Vasko96164bf2016-01-21 15:41:58 +01001184 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001185 }
1186
Michal Vasko48a63ed2016-03-01 09:48:21 +01001187 /* LOCK */
Michal Vaskof04a52a2016-04-07 10:52:10 +02001188 if (nc_ps_lock(ps)) {
1189 return -1;
1190 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001191
Michal Vasko96164bf2016-01-21 15:41:58 +01001192 for (i = 0; i < ps->session_count; ++i) {
1193 if ((ps->sessions[i]->status == NC_STATUS_RUNNING) && (ps->sessions[i]->ti_type == NC_TI_LIBSSH)
1194 && ps->sessions[i]->ti.libssh.next) {
1195 /* an SSH session with more channels */
1196 for (new_session = ps->sessions[i]->ti.libssh.next;
1197 new_session != ps->sessions[i];
1198 new_session = new_session->ti.libssh.next) {
1199 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1200 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1201 /* we found our session */
1202 break;
1203 }
1204 }
1205 if (new_session != ps->sessions[i]) {
1206 break;
1207 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001208
Michal Vasko96164bf2016-01-21 15:41:58 +01001209 new_session = NULL;
1210 }
1211 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001212
Michal Vasko48a63ed2016-03-01 09:48:21 +01001213 /* UNLOCK */
Michal Vaskof04a52a2016-04-07 10:52:10 +02001214 nc_ps_unlock(ps);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001215
Michal Vasko96164bf2016-01-21 15:41:58 +01001216 if (!new_session) {
1217 ERR("No session with a NETCONF SSH channel ready was found.");
1218 return -1;
1219 }
1220
1221 /* assign new SID atomically */
1222 pthread_spin_lock(&server_opts.sid_lock);
1223 new_session->id = server_opts.new_session_id++;
1224 pthread_spin_unlock(&server_opts.sid_lock);
Michal Vaskofb89d772016-01-08 12:25:35 +01001225
Michal Vasko086311b2016-01-08 09:53:11 +01001226 /* NETCONF handshake */
1227 if (nc_handshake(new_session)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001228 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001229 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001230
Michal Vasko086311b2016-01-08 09:53:11 +01001231 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001232 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001233
Michal Vasko96164bf2016-01-21 15:41:58 +01001234 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001235}