blob: 2055c91b6ef4af441c71f49332d792d13383baa0 [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 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of the Company nor the names of its contributors
18 * may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 *
21 */
22
23#define _GNU_SOURCE
24
25#include <stdlib.h>
26#include <string.h>
27#include <sys/types.h>
28#include <pwd.h>
29#include <shadow.h>
30#include <crypt.h>
31#include <errno.h>
32
Michal Vasko11d142a2016-01-19 15:58:24 +010033#include "session_server.h"
Michal Vaskoe22c6732016-01-29 11:03:02 +010034#include "session_server_ch.h"
35#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010036
Michal Vasko3031aae2016-01-27 16:07:18 +010037struct nc_server_ssh_opts ssh_ch_opts = {
38 .auth_methods = NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE,
39 .auth_attempts = 3,
40 .auth_timeout = 10
41};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010042pthread_mutex_t ssh_ch_opts_lock = PTHREAD_MUTEX_INITIALIZER;
Michal Vasko086311b2016-01-08 09:53:11 +010043extern struct nc_server_opts server_opts;
Michal Vaskob05053d2016-01-22 16:12:06 +010044
Michal Vasko3031aae2016-01-27 16:07:18 +010045API int
46nc_server_ssh_add_endpt_listen(const char *name, const char *address, uint16_t port)
47{
48 return nc_server_add_endpt_listen(name, address, port, NC_TI_LIBSSH);
49}
Michal Vasko086311b2016-01-08 09:53:11 +010050
Michal Vasko3031aae2016-01-27 16:07:18 +010051API int
Michal Vaskoda514772016-02-01 11:32:01 +010052nc_server_ssh_endpt_set_address(const char *endpt_name, const char *address)
53{
54 return nc_server_endpt_set_address_port(endpt_name, address, 0, NC_TI_LIBSSH);
55}
56
57API int
58nc_server_ssh_endpt_set_port(const char *endpt_name, uint16_t port)
59{
60 return nc_server_endpt_set_address_port(endpt_name, NULL, port, NC_TI_LIBSSH);
61}
62
63API int
Michal Vasko3031aae2016-01-27 16:07:18 +010064nc_server_ssh_del_endpt(const char *name)
65{
66 return nc_server_del_endpt(name, NC_TI_LIBSSH);
67}
Michal Vaskob05053d2016-01-22 16:12:06 +010068
69static int
Michal Vasko3031aae2016-01-27 16:07:18 +010070nc_server_ssh_set_hostkey(const char *privkey_path, struct nc_server_ssh_opts *opts)
Michal Vasko086311b2016-01-08 09:53:11 +010071{
Michal Vasko1a38c862016-01-15 15:50:07 +010072 if (!privkey_path) {
Michal Vasko086311b2016-01-08 09:53:11 +010073 ERRARG;
74 return -1;
75 }
76
Michal Vaskob05053d2016-01-22 16:12:06 +010077 if (!opts->sshbind) {
78 opts->sshbind = ssh_bind_new();
79 if (!opts->sshbind) {
Michal Vaskod083db62016-01-19 10:31:29 +010080 ERR("Failed to create a new ssh_bind.");
Michal Vaskob48aa812016-01-18 14:13:09 +010081 goto fail;
Michal Vasko086311b2016-01-08 09:53:11 +010082 }
83 }
84
Michal Vaskob05053d2016-01-22 16:12:06 +010085 if (ssh_bind_options_set(opts->sshbind, SSH_BIND_OPTIONS_HOSTKEY, privkey_path) != SSH_OK) {
Michal Vaskoc61c4492016-01-25 11:13:34 +010086 if (eaccess(privkey_path, R_OK)) {
87 ERR("Failed to set host key (%s).", strerror(errno));
88 } else {
89 ERR("Failed to set host key (%s).", ssh_get_error(opts->sshbind));
90 }
Michal Vaskob48aa812016-01-18 14:13:09 +010091 goto fail;
Michal Vasko086311b2016-01-08 09:53:11 +010092 }
Michal Vaskod45e25a2016-01-08 15:48:44 +010093
Michal Vaskob48aa812016-01-18 14:13:09 +010094fail:
Michal Vaskob05053d2016-01-22 16:12:06 +010095 return -1;
96}
97
98API int
Michal Vasko3031aae2016-01-27 16:07:18 +010099nc_server_ssh_endpt_set_hostkey(const char *endpt_name, const char *privkey_path)
Michal Vaskob05053d2016-01-22 16:12:06 +0100100{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100101 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100102 struct nc_endpt *endpt;
103
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100104 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100105 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100106 return -1;
107 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100108 ret = nc_server_ssh_set_hostkey(privkey_path, endpt->ti_opts);
109 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100110
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100111 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100112}
113
114API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100115nc_server_ssh_ch_set_hostkey(const char *privkey_path)
Michal Vaskob05053d2016-01-22 16:12:06 +0100116{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100117 int ret;
118
119 /* OPTS LOCK */
120 pthread_mutex_lock(&ssh_ch_opts_lock);
121 ret = nc_server_ssh_set_hostkey(privkey_path, &ssh_ch_opts);
122 /* OPTS UNLOCK */
123 pthread_mutex_unlock(&ssh_ch_opts_lock);
124
125 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100126}
127
128static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100129nc_server_ssh_set_banner(const char *banner, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100130{
Michal Vaskob05053d2016-01-22 16:12:06 +0100131 if (!banner) {
132 ERRARG;
133 return -1;
134 }
135
Michal Vaskob05053d2016-01-22 16:12:06 +0100136 if (!opts->sshbind) {
137 opts->sshbind = ssh_bind_new();
138 if (!opts->sshbind) {
139 ERR("Failed to create a new ssh_bind.");
140 goto fail;
141 }
142 }
143
144 ssh_bind_options_set(opts->sshbind, SSH_BIND_OPTIONS_BANNER, banner);
145
Michal Vaskob05053d2016-01-22 16:12:06 +0100146 return 0;
147
148fail:
Michal Vaskob48aa812016-01-18 14:13:09 +0100149 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100150}
151
152API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100153nc_server_ssh_endpt_set_banner(const char *endpt_name, const char *banner)
Michal Vasko086311b2016-01-08 09:53:11 +0100154{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100155 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100156 struct nc_endpt *endpt;
157
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100158 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100159 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100160 return -1;
161 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100162 ret = nc_server_ssh_set_banner(banner, endpt->ti_opts);
163 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100164
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100165 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100166}
167
168API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100169nc_server_ssh_ch_set_banner(const char *banner)
Michal Vasko086311b2016-01-08 09:53:11 +0100170{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100171 int ret;
172
173 /* OPTS LOCK */
174 pthread_mutex_lock(&ssh_ch_opts_lock);
175 ret = nc_server_ssh_set_banner(banner, &ssh_ch_opts);
176 /* OPTS UNLOCK */
177 pthread_mutex_unlock(&ssh_ch_opts_lock);
178
179 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100180}
181
182static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100183nc_server_ssh_set_auth_methods(int auth_methods, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100184{
Michal Vasko086311b2016-01-08 09:53:11 +0100185 if (!(auth_methods & NC_SSH_AUTH_PUBLICKEY) && !(auth_methods & NC_SSH_AUTH_PASSWORD)
186 && !(auth_methods & NC_SSH_AUTH_INTERACTIVE)) {
187 ERRARG;
188 return -1;
189 }
190
Michal Vaskob05053d2016-01-22 16:12:06 +0100191 opts->auth_methods = auth_methods;
192 return 0;
193}
194
195API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100196nc_server_ssh_endpt_set_auth_methods(const char *endpt_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100197{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100198 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100199 struct nc_endpt *endpt;
200
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100201 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100202 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100203 return -1;
204 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100205 ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->ti_opts);
206 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100207
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100208 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100209}
210
211API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100212nc_server_ssh_ch_set_auth_methods(int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100213{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100214 int ret;
215
216 /* OPTS LOCK */
217 pthread_mutex_lock(&ssh_ch_opts_lock);
218 ret = nc_server_ssh_set_auth_methods(auth_methods, &ssh_ch_opts);
219 /* OPTS UNLOCK */
220 pthread_mutex_unlock(&ssh_ch_opts_lock);
221
222 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100223}
224
225static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100226nc_server_ssh_set_auth_attempts(uint16_t auth_attempts, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100227{
Michal Vaskob05053d2016-01-22 16:12:06 +0100228 if (!auth_attempts) {
229 ERRARG;
230 return -1;
231 }
232
Michal Vaskob05053d2016-01-22 16:12:06 +0100233 opts->auth_attempts = auth_attempts;
Michal Vasko086311b2016-01-08 09:53:11 +0100234 return 0;
235}
236
237API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100238nc_server_ssh_endpt_set_auth_attempts(const char *endpt_name, uint16_t auth_attempts)
Michal Vasko086311b2016-01-08 09:53:11 +0100239{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100240 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100241 struct nc_endpt *endpt;
242
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100243 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100244 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100245 return -1;
246 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100247 ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->ti_opts);
248 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100249
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100250 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100251}
252
253API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100254nc_server_ssh_set_ch_auth_attempts(uint16_t auth_attempts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100255{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100256 int ret;
257
258 /* OPTS LOCK */
259 pthread_mutex_lock(&ssh_ch_opts_lock);
260 ret = nc_server_ssh_set_auth_attempts(auth_attempts, &ssh_ch_opts);
261 /* OPTS UNLOCK */
262 pthread_mutex_unlock(&ssh_ch_opts_lock);
263
264 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100265}
266
267static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100268nc_server_ssh_set_auth_timeout(uint16_t auth_timeout, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100269{
Michal Vaskob05053d2016-01-22 16:12:06 +0100270 if (!auth_timeout) {
Michal Vasko086311b2016-01-08 09:53:11 +0100271 ERRARG;
272 return -1;
273 }
274
Michal Vaskob05053d2016-01-22 16:12:06 +0100275 opts->auth_timeout = auth_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100276 return 0;
277}
278
279API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100280nc_server_ssh_endpt_set_auth_timeout(const char *endpt_name, uint16_t auth_timeout)
Michal Vasko086311b2016-01-08 09:53:11 +0100281{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100282 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100283 struct nc_endpt *endpt;
284
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100285 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100286 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100287 return -1;
288 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100289 ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->ti_opts);
290 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100291
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100292 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100293}
294
295API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100296nc_server_ssh_ch_set_auth_timeout(uint16_t auth_timeout)
Michal Vaskob05053d2016-01-22 16:12:06 +0100297{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100298 int ret;
299
300 /* OPTS LOCK */
301 pthread_mutex_lock(&ssh_ch_opts_lock);
302 ret = nc_server_ssh_set_auth_timeout(auth_timeout, &ssh_ch_opts);
303 /* OPTS UNLOCK */
304 pthread_mutex_unlock(&ssh_ch_opts_lock);
305
306 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100307}
308
309static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100310nc_server_ssh_add_authkey(const char *pubkey_path, const char *username, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100311{
Michal Vaskob05053d2016-01-22 16:12:06 +0100312 if (!pubkey_path || !username) {
Michal Vasko086311b2016-01-08 09:53:11 +0100313 ERRARG;
314 return -1;
315 }
316
Michal Vaskob05053d2016-01-22 16:12:06 +0100317 ++opts->authkey_count;
318 opts->authkeys = realloc(opts->authkeys, opts->authkey_count * sizeof *opts->authkeys);
319
320 nc_ctx_lock(-1, NULL);
321 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);
323 nc_ctx_unlock();
324
Michal Vasko086311b2016-01-08 09:53:11 +0100325 return 0;
326}
327
328API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100329nc_server_ssh_endpt_add_authkey(const char *endpt_name, const char *pubkey_path, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100330{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100331 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100332 struct nc_endpt *endpt;
333
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);
339 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100340
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100341 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100342}
343
344API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100345nc_server_ssh_ch_add_authkey(const char *pubkey_path, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100346{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100347 int ret;
348
349 /* OPTS LOCK */
350 pthread_mutex_lock(&ssh_ch_opts_lock);
351 ret = nc_server_ssh_add_authkey(pubkey_path, username, &ssh_ch_opts);
352 /* OPTS UNLOCK */
353 pthread_mutex_unlock(&ssh_ch_opts_lock);
354
355 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100356}
357
358static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100359nc_server_ssh_del_authkey(const char *pubkey_path, const char *username, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100360{
Michal Vasko086311b2016-01-08 09:53:11 +0100361 uint32_t i;
362 int ret = -1;
363
Michal Vasko1a38c862016-01-15 15:50:07 +0100364 if (!pubkey_path && !username) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100365 nc_ctx_lock(-1, NULL);
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 Vasko11d142a2016-01-19 15:58:24 +0100372 nc_ctx_unlock();
Michal Vaskob05053d2016-01-22 16:12:06 +0100373 free(opts->authkeys);
374 opts->authkeys = NULL;
375 opts->authkey_count = 0;
Michal Vasko1a38c862016-01-15 15:50:07 +0100376 } else {
Michal Vaskob05053d2016-01-22 16:12:06 +0100377 for (i = 0; i < opts->authkey_count; ++i) {
378 if ((!pubkey_path || !strcmp(opts->authkeys[i].path, pubkey_path))
379 && (!username || !strcmp(opts->authkeys[i].username, username))) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100380 nc_ctx_lock(-1, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +0100381 lydict_remove(server_opts.ctx, opts->authkeys[i].path);
382 lydict_remove(server_opts.ctx, opts->authkeys[i].username);
Michal Vasko11d142a2016-01-19 15:58:24 +0100383 nc_ctx_unlock();
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 Vaskoc6b9c7b2016-01-28 11:10:08 +0100407 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100408 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100409 return -1;
410 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100411 ret = nc_server_ssh_del_authkey(pubkey_path, username, endpt->ti_opts);
412 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100413
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100414 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100415}
416
417API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100418nc_server_ssh_ch_del_authkey(const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100419{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100420 int ret;
421
422 /* OPTS LOCK */
423 pthread_mutex_lock(&ssh_ch_opts_lock);
424 ret = nc_server_ssh_del_authkey(pubkey_path, username, &ssh_ch_opts);
425 /* OPTS UNLOCK */
426 pthread_mutex_unlock(&ssh_ch_opts_lock);
427
428 return ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100429}
430
431void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100432nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100433{
434 if (opts->sshbind) {
435 ssh_bind_free(opts->sshbind);
436 opts->sshbind = NULL;
437 }
438
439 nc_server_ssh_del_authkey(NULL, NULL, opts);
Michal Vaskob05053d2016-01-22 16:12:06 +0100440}
441
Michal Vasko086311b2016-01-08 09:53:11 +0100442API void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100443nc_server_ssh_ch_clear_opts(void)
Michal Vasko086311b2016-01-08 09:53:11 +0100444{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100445 /* OPTS LOCK */
446 pthread_mutex_lock(&ssh_ch_opts_lock);
447 nc_server_ssh_clear_opts(&ssh_ch_opts);
448 /* OPTS UNLOCK */
449 pthread_mutex_unlock(&ssh_ch_opts_lock);
Michal Vasko086311b2016-01-08 09:53:11 +0100450}
451
452static char *
453auth_password_get_pwd_hash(const char *username)
454{
455 struct passwd *pwd, pwd_buf;
456 struct spwd *spwd, spwd_buf;
457 char *pass_hash = NULL, buf[256];
458
459 getpwnam_r(username, &pwd_buf, buf, 256, &pwd);
460 if (!pwd) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100461 VRB("User \"%s\" not found locally.", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100462 return NULL;
463 }
464
465 if (!strcmp(pwd->pw_passwd, "x")) {
466 getspnam_r(username, &spwd_buf, buf, 256, &spwd);
467 if (!spwd) {
468 VRB("Failed to retrieve the shadow entry for \"%s\".", username);
469 return NULL;
470 }
471
472 pass_hash = spwd->sp_pwdp;
473 } else {
474 pass_hash = pwd->pw_passwd;
475 }
476
477 if (!pass_hash) {
Michal Vaskod083db62016-01-19 10:31:29 +0100478 ERR("No password could be retrieved for \"%s\".", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100479 return NULL;
480 }
481
482 /* check the hash structure for special meaning */
483 if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) {
484 VRB("User \"%s\" is not allowed to authenticate using a password.", username);
485 return NULL;
486 }
487 if (!strcmp(pass_hash, "*NP*")) {
488 VRB("Retrieving password for \"%s\" from a NIS+ server not supported.", username);
489 return NULL;
490 }
491
492 return strdup(pass_hash);
493}
494
495static int
496auth_password_compare_pwd(const char *pass_hash, const char *pass_clear)
497{
498 char *new_pass_hash;
499 struct crypt_data cdata;
500
501 if (!pass_hash[0]) {
502 if (!pass_clear[0]) {
503 WRN("User authentication successful with an empty password!");
504 return 0;
505 } else {
506 /* the user did now know he does not need any password,
507 * (which should not be used) so deny authentication */
508 return 1;
509 }
510 }
511
512 cdata.initialized = 0;
513 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
514 return strcmp(new_pass_hash, pass_hash);
515}
516
517static void
518nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
519{
520 char *pass_hash;
521
522 pass_hash = auth_password_get_pwd_hash(session->username);
523 if (pass_hash && !auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100524 VRB("User \"%s\" authenticated.", session->username);
Michal Vasko086311b2016-01-08 09:53:11 +0100525 ssh_message_auth_reply_success(msg, 0);
526 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
527 free(pass_hash);
528 return;
529 }
530
531 free(pass_hash);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100532 ++session->ssh_auth_attempts;
Michal Vaskod083db62016-01-19 10:31:29 +0100533 VRB("Failed user \"'%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100534 ssh_message_reply_default(msg);
535}
536
537static void
538nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
539{
540 char *pass_hash;
541
542 if (!ssh_message_auth_kbdint_is_response(msg)) {
543 const char *prompts[] = {"Password: "};
544 char echo[] = {0};
545
546 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
547 } else {
548 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {
549 ssh_message_reply_default(msg);
550 return;
551 }
552 pass_hash = auth_password_get_pwd_hash(session->username);
553 if (!pass_hash) {
554 ssh_message_reply_default(msg);
555 return;
556 }
557 if (!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0))) {
558 VRB("User \"%s\" authenticated.", session->username);
559 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
560 ssh_message_auth_reply_success(msg, 0);
561 } else {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100562 ++session->ssh_auth_attempts;
563 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100564 ssh_message_reply_default(msg);
565 }
566 }
567}
568
569static const char *
Michal Vasko3031aae2016-01-27 16:07:18 +0100570auth_pubkey_compare_key(struct nc_server_ssh_opts *opts, ssh_key key)
Michal Vasko086311b2016-01-08 09:53:11 +0100571{
572 uint32_t i;
573 ssh_key pub_key;
Michal Vasko76e3a352016-01-18 09:07:00 +0100574 const char *username = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100575
Michal Vasko3031aae2016-01-27 16:07:18 +0100576 for (i = 0; i < opts->authkey_count; ++i) {
577 if (ssh_pki_import_pubkey_file(opts->authkeys[i].path, &pub_key) != SSH_OK) {
578 if (eaccess(opts->authkeys[i].path, R_OK)) {
579 WRN("Failed to import the public key \"%s\" (%s).", opts->authkeys[i].path, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100580 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +0100581 WRN("Failed to import the public key \"%s\" (%s).", __func__, opts->authkeys[i].path, ssh_get_error(pub_key));
Michal Vasko086311b2016-01-08 09:53:11 +0100582 }
583 continue;
584 }
585
586 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
587 ssh_key_free(pub_key);
588 break;
589 }
590
591 ssh_key_free(pub_key);
592 }
593
Michal Vasko3031aae2016-01-27 16:07:18 +0100594 if (i < opts->authkey_count) {
595 username = opts->authkeys[i].username;
Michal Vasko086311b2016-01-08 09:53:11 +0100596 }
597
598 return username;
599}
600
601static void
602nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
603{
604 const char *username;
605 int signature_state;
606
607 signature_state = ssh_message_auth_publickey_state(msg);
608 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
609 VRB("User \"%s\" authenticated.", session->username);
610 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
611 ssh_message_auth_reply_success(msg, 0);
612 return;
613
614 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100615 if ((username = auth_pubkey_compare_key(session->ti_opts, ssh_message_auth_pubkey(msg))) == NULL) {
Michal Vasko086311b2016-01-08 09:53:11 +0100616 VRB("User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
617
618 } else if (strcmp(session->username, username)) {
619 VRB("User \"%s\" is not the username identified with the presented public key.", session->username);
620
621 } else {
622 /* accepting only the use of a public key */
623 ssh_message_auth_reply_pk_ok_simple(msg);
624 return;
625 }
626 }
627
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100628 ++session->ssh_auth_attempts;
629 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100630 ssh_message_reply_default(msg);
631}
632
633static int
Michal Vasko96164bf2016-01-21 15:41:58 +0100634nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100635{
Michal Vasko96164bf2016-01-21 15:41:58 +0100636 ssh_channel chan;
637
638 /* first channel request */
639 if (!session->ti.libssh.channel) {
640 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100641 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100642 return -1;
643 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100644 chan = ssh_message_channel_request_open_reply_accept(msg);
645 if (!chan) {
646 ERR("Failed to create a new SSH channel.");
647 return -1;
648 }
649 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +0100650
Michal Vasko96164bf2016-01-21 15:41:58 +0100651 /* additional channel request */
652 } else {
653 chan = ssh_message_channel_request_open_reply_accept(msg);
654 if (!chan) {
655 ERR("Session %u: failed to create a new SSH channel.", session->id);
656 return -1;
657 }
658 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +0100659 }
660
Michal Vasko086311b2016-01-08 09:53:11 +0100661 return 0;
662}
663
664static int
665nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
666{
Michal Vasko96164bf2016-01-21 15:41:58 +0100667 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +0100668
Michal Vasko96164bf2016-01-21 15:41:58 +0100669 if (strcmp(subsystem, "netconf")) {
670 WRN("Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +0100671 return -1;
672 }
673
Michal Vasko96164bf2016-01-21 15:41:58 +0100674 if (session->ti.libssh.channel == channel) {
675 /* first channel requested */
676 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
677 ERRINT;
678 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100679 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100680 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
681 ERR("Session %u: subsystem \"netconf\" requested for the second time.", session->id);
682 return -1;
683 }
684
685 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +0100686 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +0100687 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
688 new_session = calloc(1, sizeof *new_session);
689
690 /* insert the new session */
691 if (!session->ti.libssh.next) {
692 new_session->ti.libssh.next = session;
693 } else {
694 new_session->ti.libssh.next = session->ti.libssh.next;
695 }
696 session->ti.libssh.next = new_session;
697
698 new_session->status = NC_STATUS_STARTING;
699 new_session->side = NC_SERVER;
700 new_session->ti_type = NC_TI_LIBSSH;
701 new_session->ti_lock = session->ti_lock;
702 new_session->ti.libssh.channel = channel;
703 new_session->ti.libssh.session = session->ti.libssh.session;
704 new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
705 new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
706 new_session->port = session->port;
707 new_session->ctx = server_opts.ctx;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100708 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX
709 | (session->flags & NC_SESSION_CALLHOME ? NC_SESSION_CALLHOME : 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100710 }
711
712 return 0;
713}
714
Michal Vasko96164bf2016-01-21 15:41:58 +0100715int
Michal Vaskob48aa812016-01-18 14:13:09 +0100716nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +0100717{
718 const char *str_type, *str_subtype = NULL, *username;
719 int subtype, type;
720 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +0100721
722 type = ssh_message_type(msg);
723 subtype = ssh_message_subtype(msg);
724
725 switch (type) {
726 case SSH_REQUEST_AUTH:
727 str_type = "request-auth";
728 switch (subtype) {
729 case SSH_AUTH_METHOD_NONE:
730 str_subtype = "none";
731 break;
732 case SSH_AUTH_METHOD_PASSWORD:
733 str_subtype = "password";
734 break;
735 case SSH_AUTH_METHOD_PUBLICKEY:
736 str_subtype = "publickey";
737 break;
738 case SSH_AUTH_METHOD_HOSTBASED:
739 str_subtype = "hostbased";
740 break;
741 case SSH_AUTH_METHOD_INTERACTIVE:
742 str_subtype = "interactive";
743 break;
744 case SSH_AUTH_METHOD_GSSAPI_MIC:
745 str_subtype = "gssapi-mic";
746 break;
747 }
748 break;
749
750 case SSH_REQUEST_CHANNEL_OPEN:
751 str_type = "request-channel-open";
752 switch (subtype) {
753 case SSH_CHANNEL_SESSION:
754 str_subtype = "session";
755 break;
756 case SSH_CHANNEL_DIRECT_TCPIP:
757 str_subtype = "direct-tcpip";
758 break;
759 case SSH_CHANNEL_FORWARDED_TCPIP:
760 str_subtype = "forwarded-tcpip";
761 break;
762 case (int)SSH_CHANNEL_X11:
763 str_subtype = "channel-x11";
764 break;
765 case SSH_CHANNEL_UNKNOWN:
766 /* fallthrough */
767 default:
768 str_subtype = "unknown";
769 break;
770 }
771 break;
772
773 case SSH_REQUEST_CHANNEL:
774 str_type = "request-channel";
775 switch (subtype) {
776 case SSH_CHANNEL_REQUEST_PTY:
777 str_subtype = "pty";
778 break;
779 case SSH_CHANNEL_REQUEST_EXEC:
780 str_subtype = "exec";
781 break;
782 case SSH_CHANNEL_REQUEST_SHELL:
783 str_subtype = "shell";
784 break;
785 case SSH_CHANNEL_REQUEST_ENV:
786 str_subtype = "env";
787 break;
788 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
789 str_subtype = "subsystem";
790 break;
791 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
792 str_subtype = "window-change";
793 break;
794 case SSH_CHANNEL_REQUEST_X11:
795 str_subtype = "x11";
796 break;
797 case SSH_CHANNEL_REQUEST_UNKNOWN:
798 /* fallthrough */
799 default:
800 str_subtype = "unknown";
801 break;
802 }
803 break;
804
805 case SSH_REQUEST_SERVICE:
806 str_type = "request-service";
807 str_subtype = ssh_message_service_service(msg);
808 break;
809
810 case SSH_REQUEST_GLOBAL:
811 str_type = "request-global";
812 switch (subtype) {
813 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
814 str_subtype = "tcpip-forward";
815 break;
816 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
817 str_subtype = "cancel-tcpip-forward";
818 break;
819 case SSH_GLOBAL_REQUEST_UNKNOWN:
820 /* fallthrough */
821 default:
822 str_subtype = "unknown";
823 break;
824 }
825 break;
826
827 default:
828 str_type = "unknown";
829 str_subtype = "unknown";
830 break;
831 }
832
833 VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vasko96164bf2016-01-21 15:41:58 +0100834 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +0100835
836 /*
837 * process known messages
838 */
839 if (type == SSH_REQUEST_AUTH) {
840 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
841 ERR("User \"%s\" authenticated, but requested another authentication.", session->username);
842 ssh_message_reply_default(msg);
843 return 0;
844 }
845
Michal Vasko3031aae2016-01-27 16:07:18 +0100846 if (session->ssh_auth_attempts >= ((struct nc_server_ssh_opts *)session->ti_opts)->auth_attempts) {
Michal Vasko086311b2016-01-08 09:53:11 +0100847 /* too many failed attempts */
848 ssh_message_reply_default(msg);
849 return 0;
850 }
851
852 /* save the username, do not let the client change it */
853 username = ssh_message_auth_user(msg);
854 if (!session->username) {
855 if (!username) {
856 ERR("Denying an auth request without a username.");
857 return 1;
858 }
859
Michal Vasko05ba9df2016-01-13 14:40:27 +0100860 session->username = lydict_insert(server_opts.ctx, username, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100861 } else if (username) {
862 if (strcmp(username, session->username)) {
863 ERR("User \"%s\" changed its username to \"%s\".", session->username, username);
864 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +0100865 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +0100866 return 1;
867 }
868 }
869
870 if (subtype == SSH_AUTH_METHOD_NONE) {
871 /* libssh will return the supported auth methods */
872 return 1;
873 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
874 nc_sshcb_auth_password(session, msg);
875 return 0;
876 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
877 nc_sshcb_auth_pubkey(session, msg);
878 return 0;
879 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
880 nc_sshcb_auth_kbdint(session, msg);
881 return 0;
882 }
883 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +0100884 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100885 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100886 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100887 }
Michal Vasko086311b2016-01-08 09:53:11 +0100888 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +0100889
Michal Vasko0df67562016-01-21 15:50:11 +0100890 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100891 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
892 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +0100893 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +0100894 } else {
895 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100896 }
897 return 0;
898 }
899 }
900
901 /* we did not process it */
902 return 1;
903}
904
Michal Vasko1a38c862016-01-15 15:50:07 +0100905/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +0100906static int
907nc_open_netconf_channel(struct nc_session *session, int timeout)
908{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100909 int elapsed = 0, ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100910
911 /* message callback is executed twice to give chance for the channel to be
912 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100913 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +0100914 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100915 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +0100916 return -1;
917 }
918
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100919 ret = nc_timedlock(session->ti_lock, timeout, NULL);
920 if (ret != 1) {
921 return ret;
922 }
923
924 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
925 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100926 ERR("Failed to receive SSH messages on a session (%s).",
927 ssh_get_error(session->ti.libssh.session));
Michal Vasko11d142a2016-01-19 15:58:24 +0100928 pthread_mutex_unlock(session->ti_lock);
Michal Vasko086311b2016-01-08 09:53:11 +0100929 return -1;
930 }
931
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100932 if (!session->ti.libssh.channel) {
933 /* we did not receive channel-open, timeout */
934 pthread_mutex_unlock(session->ti_lock);
935 return 0;
936 }
937
938 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
939 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100940 ERR("Failed to receive SSH messages on a session (%s).",
941 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100942 pthread_mutex_unlock(session->ti_lock);
943 return -1;
944 }
945 pthread_mutex_unlock(session->ti_lock);
946
947 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
948 /* we did not receive subsystem-request, timeout */
949 return 0;
950 }
951
952 return 1;
953 }
954
955 while (1) {
956 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100957 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100958 return -1;
959 }
960
961 ret = nc_timedlock(session->ti_lock, timeout, &elapsed);
962 if (ret != 1) {
963 return ret;
964 }
965
966 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
967 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100968 ERR("Failed to receive SSH messages on a session (%s).",
969 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100970 pthread_mutex_unlock(session->ti_lock);
971 return -1;
972 }
973
974 pthread_mutex_unlock(session->ti_lock);
975
Michal Vasko086311b2016-01-08 09:53:11 +0100976 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100977 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +0100978 }
979
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100980 if ((timeout != -1) && (timeout >= elapsed)) {
981 /* timeout */
Michal Vasko086311b2016-01-08 09:53:11 +0100982 break;
983 }
984
Michal Vasko086311b2016-01-08 09:53:11 +0100985 usleep(NC_TIMEOUT_STEP);
986 elapsed += NC_TIMEOUT_STEP;
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100987 }
Michal Vasko086311b2016-01-08 09:53:11 +0100988
Michal Vasko1a38c862016-01-15 15:50:07 +0100989 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100990}
991
Michal Vasko96164bf2016-01-21 15:41:58 +0100992/* ret 0 - timeout, 1 channel has data, 2 some other channel has data,
993 * 3 status change, 4 new SSH message, 5 new NETCONF SSH channel, -1 error */
994int
995nc_ssh_pollin(struct nc_session *session, int *timeout)
996{
997 int ret, elapsed = 0;
998 struct nc_session *new;
999
1000 ret = nc_timedlock(session->ti_lock, *timeout, &elapsed);
1001 if (*timeout > 0) {
1002 *timeout -= elapsed;
1003 }
1004
1005 if (ret != 1) {
1006 return ret;
1007 }
1008
1009 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1010 pthread_mutex_unlock(session->ti_lock);
1011
1012 if (ret != SSH_OK) {
1013 ERR("Session %u: failed to receive SSH messages (%s).", session->id,
1014 ssh_get_error(session->ti.libssh.session));
1015 session->status = NC_STATUS_INVALID;
1016 session->term_reason = NC_SESSION_TERM_OTHER;
1017 return 3;
1018 }
1019
1020 /* new SSH message */
1021 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1022 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1023 if (session->ti.libssh.next) {
1024 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1025 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1026 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1027 /* new NETCONF SSH channel */
1028 return 5;
1029 }
1030 }
1031 }
1032
1033 /* just some SSH message */
1034 return 4;
1035 }
1036
1037 /* no new SSH message, maybe NETCONF data? */
1038 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
1039 /* not this one */
1040 if (!ret) {
1041 return 2;
1042 } else if (ret == SSH_ERROR) {
1043 ERR("Session %u: SSH channel error (%s).", session->id,
1044 ssh_get_error(session->ti.libssh.session));
1045 session->status = NC_STATUS_INVALID;
1046 session->term_reason = NC_SESSION_TERM_OTHER;
1047 return 3;
1048 } else if (ret == SSH_EOF) {
1049 ERR("Session %u: communication channel unexpectedly closed (libssh).",
1050 session->id);
1051 session->status = NC_STATUS_INVALID;
1052 session->term_reason = NC_SESSION_TERM_DROPPED;
1053 return 3;
1054 }
1055
1056 return 1;
1057}
1058
Michal Vasko3031aae2016-01-27 16:07:18 +01001059API int
1060nc_connect_callhome_ssh(const char *host, uint16_t port, int timeout, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001061{
Michal Vasko3031aae2016-01-27 16:07:18 +01001062 return nc_connect_callhome(host, port, NC_TI_LIBSSH, timeout, session);
1063}
1064
1065int
1066nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
1067{
1068 struct nc_server_ssh_opts *opts;
Michal Vasko1a38c862016-01-15 15:50:07 +01001069 int libssh_auth_methods = 0, elapsed = 0, ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001070
Michal Vasko3031aae2016-01-27 16:07:18 +01001071 opts = session->ti_opts;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001072
Michal Vasko086311b2016-01-08 09:53:11 +01001073 /* other transport-specific data */
1074 session->ti_type = NC_TI_LIBSSH;
1075 session->ti.libssh.session = ssh_new();
1076 if (!session->ti.libssh.session) {
Michal Vaskod083db62016-01-19 10:31:29 +01001077 ERR("Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001078 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001079 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001080 }
1081
Michal Vaskoc61c4492016-01-25 11:13:34 +01001082 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001083 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1084 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001085 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001086 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1087 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001088 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001089 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1090 }
1091 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1092
1093 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001094 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001095 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001096
Michal Vaskoc61c4492016-01-25 11:13:34 +01001097 if (ssh_bind_accept_fd(opts->sshbind, session->ti.libssh.session, sock) == SSH_ERROR) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001098 ERR("SSH failed to accept a new connection (%s).", ssh_get_error(opts->sshbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001099 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001100 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001101 }
1102
1103 if (ssh_handle_key_exchange(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001104 ERR("SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001105 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001106 }
1107
1108 /* authenticate */
1109 do {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001110 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001111 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001112 return -1;
1113 }
1114
Michal Vasko086311b2016-01-08 09:53:11 +01001115 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001116 ERR("Failed to receive SSH messages on a session (%s).",
1117 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001118 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001119 }
1120
1121 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1122 break;
1123 }
1124
1125 usleep(NC_TIMEOUT_STEP);
1126 elapsed += NC_TIMEOUT_STEP;
1127 } while ((timeout == -1) || (timeout && (elapsed < timeout)));
1128
1129 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1130 /* timeout */
Michal Vasko1a38c862016-01-15 15:50:07 +01001131 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001132 }
1133
1134 if (timeout > 0) {
1135 timeout -= elapsed;
1136 }
1137
1138 /* open channel */
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001139 ret = nc_open_netconf_channel(session, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001140 if (ret < 1) {
1141 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001142 }
1143
Michal Vasko96164bf2016-01-21 15:41:58 +01001144 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1145
Michal Vasko1a38c862016-01-15 15:50:07 +01001146 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001147}
1148
Michal Vasko96164bf2016-01-21 15:41:58 +01001149API int
1150nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001151{
Michal Vasko96164bf2016-01-21 15:41:58 +01001152 struct nc_session *new_session = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001153 uint16_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001154
Michal Vasko96164bf2016-01-21 15:41:58 +01001155 if (!ps || !session) {
1156 ERRARG;
1157 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001158 }
1159
Michal Vasko96164bf2016-01-21 15:41:58 +01001160 for (i = 0; i < ps->session_count; ++i) {
1161 if ((ps->sessions[i]->status == NC_STATUS_RUNNING) && (ps->sessions[i]->ti_type == NC_TI_LIBSSH)
1162 && ps->sessions[i]->ti.libssh.next) {
1163 /* an SSH session with more channels */
1164 for (new_session = ps->sessions[i]->ti.libssh.next;
1165 new_session != ps->sessions[i];
1166 new_session = new_session->ti.libssh.next) {
1167 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1168 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1169 /* we found our session */
1170 break;
1171 }
1172 }
1173 if (new_session != ps->sessions[i]) {
1174 break;
1175 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001176
Michal Vasko96164bf2016-01-21 15:41:58 +01001177 new_session = NULL;
1178 }
1179 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001180
Michal Vasko96164bf2016-01-21 15:41:58 +01001181 if (!new_session) {
1182 ERR("No session with a NETCONF SSH channel ready was found.");
1183 return -1;
1184 }
1185
1186 /* assign new SID atomically */
1187 pthread_spin_lock(&server_opts.sid_lock);
1188 new_session->id = server_opts.new_session_id++;
1189 pthread_spin_unlock(&server_opts.sid_lock);
Michal Vaskofb89d772016-01-08 12:25:35 +01001190
Michal Vasko086311b2016-01-08 09:53:11 +01001191 /* NETCONF handshake */
1192 if (nc_handshake(new_session)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001193 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001194 }
1195 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001196 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001197
Michal Vasko96164bf2016-01-21 15:41:58 +01001198 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001199}