blob: 2d0f5d678620b6b5f41c5e6def1052d0ffa94b98 [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
Michal Vaskoda514772016-02-01 11:32:01 +010038nc_server_ssh_endpt_set_address(const char *endpt_name, const char *address)
39{
40 return nc_server_endpt_set_address_port(endpt_name, address, 0, NC_TI_LIBSSH);
41}
42
43API int
44nc_server_ssh_endpt_set_port(const char *endpt_name, uint16_t port)
45{
46 return nc_server_endpt_set_address_port(endpt_name, NULL, port, NC_TI_LIBSSH);
47}
48
Michal Vaskob05053d2016-01-22 16:12:06 +010049static int
Michal Vaskoe2713da2016-08-22 16:06:40 +020050nc_server_ssh_add_hostkey(const char *privkey_path, struct nc_server_ssh_opts *opts)
Michal Vasko086311b2016-01-08 09:53:11 +010051{
Michal Vasko1a38c862016-01-15 15:50:07 +010052 if (!privkey_path) {
Michal Vasko45e53ae2016-04-07 11:46:03 +020053 ERRARG("privkey_path");
Michal Vasko086311b2016-01-08 09:53:11 +010054 return -1;
55 }
56
Michal Vaskoe2713da2016-08-22 16:06:40 +020057 if (eaccess(privkey_path, R_OK)) {
58 ERR("Host key \"%s\" cannot be read (%s).", privkey_path, strerror(errno));
Michal Vasko5fcc7142016-02-02 12:21:10 +010059 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +010060 }
Michal Vaskod45e25a2016-01-08 15:48:44 +010061
Michal Vaskoe2713da2016-08-22 16:06:40 +020062 ++opts->hostkey_count;
63 opts->hostkeys = nc_realloc(opts->hostkeys, opts->hostkey_count * sizeof *opts->hostkeys);
64 if (!opts->hostkeys) {
65 ERRMEM;
66 return -1;
67 }
68 opts->hostkeys[opts->hostkey_count - 1] = lydict_insert(server_opts.ctx, privkey_path, 0);
69
Michal Vasko5fcc7142016-02-02 12:21:10 +010070 return 0;
Michal Vaskob05053d2016-01-22 16:12:06 +010071}
72
73API int
Michal Vaskoe2713da2016-08-22 16:06:40 +020074nc_server_ssh_endpt_add_hostkey(const char *endpt_name, const char *privkey_path)
Michal Vaskob05053d2016-01-22 16:12:06 +010075{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010076 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +010077 struct nc_endpt *endpt;
78
Michal Vasko51e514d2016-02-02 15:51:52 +010079 /* LOCK */
Michal Vaskoe2713da2016-08-22 16:06:40 +020080 endpt = nc_server_endpt_lock(endpt_name, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +010081 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +010082 return -1;
83 }
Michal Vaskoe2713da2016-08-22 16:06:40 +020084 ret = nc_server_ssh_add_hostkey(privkey_path, endpt->ssh_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +010085 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010086 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +010087
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010088 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +010089}
90
91API int
Michal Vaskoe2713da2016-08-22 16:06:40 +020092nc_server_ssh_ch_add_hostkey(const char *privkey_path)
Michal Vaskob05053d2016-01-22 16:12:06 +010093{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010094 int ret;
95
96 /* OPTS LOCK */
97 pthread_mutex_lock(&ssh_ch_opts_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +020098 ret = nc_server_ssh_add_hostkey(privkey_path, &ssh_ch_opts);
99 /* OPTS UNLOCK */
100 pthread_mutex_unlock(&ssh_ch_opts_lock);
101
102 return ret;
103}
104
105static int
106nc_server_ssh_del_hostkey(const char *privkey_path, struct nc_server_ssh_opts *opts)
107{
108 uint8_t i;
109
110 if (!privkey_path) {
111 for (i = 0; i < opts->hostkey_count; ++i) {
112 lydict_remove(server_opts.ctx, opts->hostkeys[i]);
113 }
114 free(opts->hostkeys);
115 opts->hostkeys = NULL;
116 opts->hostkey_count = 0;
117 } else {
118 for (i = 0; i < opts->hostkey_count; ++i) {
119 if (!strcmp(opts->hostkeys[i], privkey_path)) {
120 --opts->hostkey_count;
121 lydict_remove(server_opts.ctx, opts->hostkeys[i]);
122 if (i < opts->hostkey_count - 1) {
123 memmove(opts->hostkeys + i, opts->hostkeys + i + 1, (opts->hostkey_count - i) * sizeof *opts->hostkeys);
124 }
125 return 0;
126 }
127 }
128
129 ERR("Host key \"%s\" not found.", privkey_path);
130 return -1;
131 }
132
133 return 0;
134}
135
136API int
137nc_server_ssh_endpt_del_hostkey(const char *endpt_name, const char *privkey_path)
138{
139 int ret;
140 struct nc_endpt *endpt;
141
142 /* LOCK */
143 endpt = nc_server_endpt_lock(endpt_name, NULL);
144 if (!endpt) {
145 return -1;
146 }
147 ret = nc_server_ssh_del_hostkey(privkey_path, endpt->ssh_opts);
148 /* UNLOCK */
149 nc_server_endpt_unlock(endpt);
150
151 return ret;
152}
153
154API int
155nc_server_ssh_ch_del_hostkey(const char *privkey_path)
156{
157 int ret;
158
159 /* OPTS LOCK */
160 pthread_mutex_lock(&ssh_ch_opts_lock);
161 ret = nc_server_ssh_del_hostkey(privkey_path, &ssh_ch_opts);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100162 /* OPTS UNLOCK */
163 pthread_mutex_unlock(&ssh_ch_opts_lock);
164
165 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100166}
167
168static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100169nc_server_ssh_set_banner(const char *banner, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100170{
Michal Vaskob05053d2016-01-22 16:12:06 +0100171 if (!banner) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200172 ERRARG("banner");
Michal Vaskob05053d2016-01-22 16:12:06 +0100173 return -1;
174 }
175
Michal Vaskoe2713da2016-08-22 16:06:40 +0200176 if (opts->banner) {
177 lydict_remove(server_opts.ctx, opts->banner);
Michal Vaskob05053d2016-01-22 16:12:06 +0100178 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200179 opts->banner = lydict_insert(server_opts.ctx, banner, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +0100180 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100181}
182
183API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100184nc_server_ssh_endpt_set_banner(const char *endpt_name, const char *banner)
Michal Vasko086311b2016-01-08 09:53:11 +0100185{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100186 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100187 struct nc_endpt *endpt;
188
Michal Vasko51e514d2016-02-02 15:51:52 +0100189 /* LOCK */
Michal Vaskoe2713da2016-08-22 16:06:40 +0200190 endpt = nc_server_endpt_lock(endpt_name, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100191 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100192 return -1;
193 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200194 ret = nc_server_ssh_set_banner(banner, endpt->ssh_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100195 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100196 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100197
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100198 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100199}
200
201API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100202nc_server_ssh_ch_set_banner(const char *banner)
Michal Vasko086311b2016-01-08 09:53:11 +0100203{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100204 int ret;
205
206 /* OPTS LOCK */
207 pthread_mutex_lock(&ssh_ch_opts_lock);
208 ret = nc_server_ssh_set_banner(banner, &ssh_ch_opts);
209 /* OPTS UNLOCK */
210 pthread_mutex_unlock(&ssh_ch_opts_lock);
211
212 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100213}
214
215static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100216nc_server_ssh_set_auth_methods(int auth_methods, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100217{
Michal Vasko086311b2016-01-08 09:53:11 +0100218 if (!(auth_methods & NC_SSH_AUTH_PUBLICKEY) && !(auth_methods & NC_SSH_AUTH_PASSWORD)
219 && !(auth_methods & NC_SSH_AUTH_INTERACTIVE)) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200220 ERRARG("auth_methods");
Michal Vasko086311b2016-01-08 09:53:11 +0100221 return -1;
222 }
223
Michal Vaskob05053d2016-01-22 16:12:06 +0100224 opts->auth_methods = auth_methods;
225 return 0;
226}
227
228API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100229nc_server_ssh_endpt_set_auth_methods(const char *endpt_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100230{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100231 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100232 struct nc_endpt *endpt;
233
Michal Vasko51e514d2016-02-02 15:51:52 +0100234 /* LOCK */
Michal Vaskoe2713da2016-08-22 16:06:40 +0200235 endpt = nc_server_endpt_lock(endpt_name, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100236 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100237 return -1;
238 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200239 ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->ssh_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100240 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100241 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100242
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100243 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100244}
245
246API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100247nc_server_ssh_ch_set_auth_methods(int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100248{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100249 int ret;
250
251 /* OPTS LOCK */
252 pthread_mutex_lock(&ssh_ch_opts_lock);
253 ret = nc_server_ssh_set_auth_methods(auth_methods, &ssh_ch_opts);
254 /* OPTS UNLOCK */
255 pthread_mutex_unlock(&ssh_ch_opts_lock);
256
257 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100258}
259
260static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100261nc_server_ssh_set_auth_attempts(uint16_t auth_attempts, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100262{
Michal Vaskob05053d2016-01-22 16:12:06 +0100263 if (!auth_attempts) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200264 ERRARG("auth_attempts");
Michal Vaskob05053d2016-01-22 16:12:06 +0100265 return -1;
266 }
267
Michal Vaskob05053d2016-01-22 16:12:06 +0100268 opts->auth_attempts = auth_attempts;
Michal Vasko086311b2016-01-08 09:53:11 +0100269 return 0;
270}
271
272API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100273nc_server_ssh_endpt_set_auth_attempts(const char *endpt_name, uint16_t auth_attempts)
Michal Vasko086311b2016-01-08 09:53:11 +0100274{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100275 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100276 struct nc_endpt *endpt;
277
Michal Vasko51e514d2016-02-02 15:51:52 +0100278 /* LOCK */
Michal Vaskoe2713da2016-08-22 16:06:40 +0200279 endpt = nc_server_endpt_lock(endpt_name, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100280 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100281 return -1;
282 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200283 ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->ssh_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100284 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100285 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100286
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100287 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100288}
289
290API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100291nc_server_ssh_set_ch_auth_attempts(uint16_t auth_attempts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100292{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100293 int ret;
294
295 /* OPTS LOCK */
296 pthread_mutex_lock(&ssh_ch_opts_lock);
297 ret = nc_server_ssh_set_auth_attempts(auth_attempts, &ssh_ch_opts);
298 /* OPTS UNLOCK */
299 pthread_mutex_unlock(&ssh_ch_opts_lock);
300
301 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100302}
303
304static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100305nc_server_ssh_set_auth_timeout(uint16_t auth_timeout, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100306{
Michal Vaskob05053d2016-01-22 16:12:06 +0100307 if (!auth_timeout) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200308 ERRARG("auth_timeout");
Michal Vasko086311b2016-01-08 09:53:11 +0100309 return -1;
310 }
311
Michal Vaskob05053d2016-01-22 16:12:06 +0100312 opts->auth_timeout = auth_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100313 return 0;
314}
315
316API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100317nc_server_ssh_endpt_set_auth_timeout(const char *endpt_name, uint16_t auth_timeout)
Michal Vasko086311b2016-01-08 09:53:11 +0100318{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100319 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100320 struct nc_endpt *endpt;
321
Michal Vasko51e514d2016-02-02 15:51:52 +0100322 /* LOCK */
Michal Vaskoe2713da2016-08-22 16:06:40 +0200323 endpt = nc_server_endpt_lock(endpt_name, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100324 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100325 return -1;
326 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200327 ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->ssh_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100328 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100329 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100330
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100331 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100332}
333
334API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100335nc_server_ssh_ch_set_auth_timeout(uint16_t auth_timeout)
Michal Vaskob05053d2016-01-22 16:12:06 +0100336{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100337 int ret;
338
339 /* OPTS LOCK */
340 pthread_mutex_lock(&ssh_ch_opts_lock);
341 ret = nc_server_ssh_set_auth_timeout(auth_timeout, &ssh_ch_opts);
342 /* OPTS UNLOCK */
343 pthread_mutex_unlock(&ssh_ch_opts_lock);
344
345 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100346}
347
348static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100349nc_server_ssh_add_authkey(const char *pubkey_path, const char *username, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100350{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200351 if (!pubkey_path) {
352 ERRARG("pubkey_path");
353 return -1;
354 } else if (!username) {
355 ERRARG("username");
Michal Vasko086311b2016-01-08 09:53:11 +0100356 return -1;
357 }
358
Michal Vaskob05053d2016-01-22 16:12:06 +0100359 ++opts->authkey_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100360 opts->authkeys = nc_realloc(opts->authkeys, opts->authkey_count * sizeof *opts->authkeys);
361 if (!opts->authkeys) {
362 ERRMEM;
363 return -1;
364 }
Michal Vaskob05053d2016-01-22 16:12:06 +0100365 opts->authkeys[opts->authkey_count - 1].path = lydict_insert(server_opts.ctx, pubkey_path, 0);
366 opts->authkeys[opts->authkey_count - 1].username = lydict_insert(server_opts.ctx, username, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +0100367
Michal Vasko086311b2016-01-08 09:53:11 +0100368 return 0;
369}
370
371API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100372nc_server_ssh_endpt_add_authkey(const char *endpt_name, const char *pubkey_path, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100373{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100374 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100375 struct nc_endpt *endpt;
376
Michal Vasko51e514d2016-02-02 15:51:52 +0100377 /* LOCK */
Michal Vaskoe2713da2016-08-22 16:06:40 +0200378 endpt = nc_server_endpt_lock(endpt_name, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100379 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100380 return -1;
381 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200382 ret = nc_server_ssh_add_authkey(pubkey_path, username, endpt->ssh_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100383 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100384 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100385
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100386 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100387}
388
389API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100390nc_server_ssh_ch_add_authkey(const char *pubkey_path, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100391{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100392 int ret;
393
394 /* OPTS LOCK */
395 pthread_mutex_lock(&ssh_ch_opts_lock);
396 ret = nc_server_ssh_add_authkey(pubkey_path, username, &ssh_ch_opts);
397 /* OPTS UNLOCK */
398 pthread_mutex_unlock(&ssh_ch_opts_lock);
399
400 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100401}
402
403static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100404nc_server_ssh_del_authkey(const char *pubkey_path, const char *username, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100405{
Michal Vasko086311b2016-01-08 09:53:11 +0100406 uint32_t i;
407 int ret = -1;
408
Michal Vasko1a38c862016-01-15 15:50:07 +0100409 if (!pubkey_path && !username) {
Michal Vaskob05053d2016-01-22 16:12:06 +0100410 for (i = 0; i < opts->authkey_count; ++i) {
411 lydict_remove(server_opts.ctx, opts->authkeys[i].path);
412 lydict_remove(server_opts.ctx, opts->authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100413
Michal Vasko086311b2016-01-08 09:53:11 +0100414 ret = 0;
415 }
Michal Vaskob05053d2016-01-22 16:12:06 +0100416 free(opts->authkeys);
417 opts->authkeys = NULL;
418 opts->authkey_count = 0;
Michal Vasko1a38c862016-01-15 15:50:07 +0100419 } else {
Michal Vaskob05053d2016-01-22 16:12:06 +0100420 for (i = 0; i < opts->authkey_count; ++i) {
421 if ((!pubkey_path || !strcmp(opts->authkeys[i].path, pubkey_path))
422 && (!username || !strcmp(opts->authkeys[i].username, username))) {
Michal Vaskob05053d2016-01-22 16:12:06 +0100423 lydict_remove(server_opts.ctx, opts->authkeys[i].path);
424 lydict_remove(server_opts.ctx, opts->authkeys[i].username);
Michal Vasko1a38c862016-01-15 15:50:07 +0100425
Michal Vaskob05053d2016-01-22 16:12:06 +0100426 --opts->authkey_count;
Michal Vaskoc0256492016-02-02 12:19:06 +0100427 if (i < opts->authkey_count) {
428 memcpy(&opts->authkeys[i], &opts->authkeys[opts->authkey_count], sizeof *opts->authkeys);
429 } else if (!opts->authkey_count) {
430 free(opts->authkeys);
431 opts->authkeys = NULL;
432 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100433
434 ret = 0;
435 }
436 }
Michal Vasko086311b2016-01-08 09:53:11 +0100437 }
438
439 return ret;
440}
441
Michal Vaskob05053d2016-01-22 16:12:06 +0100442API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100443nc_server_ssh_endpt_del_authkey(const char *endpt_name, const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100444{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100445 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100446 struct nc_endpt *endpt;
447
Michal Vasko51e514d2016-02-02 15:51:52 +0100448 /* LOCK */
Michal Vaskoe2713da2016-08-22 16:06:40 +0200449 endpt = nc_server_endpt_lock(endpt_name, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100450 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100451 return -1;
452 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200453 ret = nc_server_ssh_del_authkey(pubkey_path, username, endpt->ssh_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100454 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100455 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100456
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100457 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100458}
459
460API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100461nc_server_ssh_ch_del_authkey(const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100462{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100463 int ret;
464
465 /* OPTS LOCK */
466 pthread_mutex_lock(&ssh_ch_opts_lock);
467 ret = nc_server_ssh_del_authkey(pubkey_path, username, &ssh_ch_opts);
468 /* OPTS UNLOCK */
469 pthread_mutex_unlock(&ssh_ch_opts_lock);
470
471 return ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100472}
473
474void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100475nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100476{
Michal Vaskoe2713da2016-08-22 16:06:40 +0200477 nc_server_ssh_del_hostkey(NULL, opts);
478 if (opts->banner) {
479 lydict_remove(server_opts.ctx, opts->banner);
480 opts->banner = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +0100481 }
Michal Vasko3031aae2016-01-27 16:07:18 +0100482 nc_server_ssh_del_authkey(NULL, NULL, opts);
Michal Vaskob05053d2016-01-22 16:12:06 +0100483}
484
Michal Vasko086311b2016-01-08 09:53:11 +0100485API void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100486nc_server_ssh_ch_clear_opts(void)
Michal Vasko086311b2016-01-08 09:53:11 +0100487{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100488 /* OPTS LOCK */
489 pthread_mutex_lock(&ssh_ch_opts_lock);
490 nc_server_ssh_clear_opts(&ssh_ch_opts);
491 /* OPTS UNLOCK */
492 pthread_mutex_unlock(&ssh_ch_opts_lock);
Michal Vasko086311b2016-01-08 09:53:11 +0100493}
494
495static char *
496auth_password_get_pwd_hash(const char *username)
497{
498 struct passwd *pwd, pwd_buf;
499 struct spwd *spwd, spwd_buf;
500 char *pass_hash = NULL, buf[256];
501
502 getpwnam_r(username, &pwd_buf, buf, 256, &pwd);
503 if (!pwd) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100504 VRB("User \"%s\" not found locally.", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100505 return NULL;
506 }
507
508 if (!strcmp(pwd->pw_passwd, "x")) {
509 getspnam_r(username, &spwd_buf, buf, 256, &spwd);
510 if (!spwd) {
511 VRB("Failed to retrieve the shadow entry for \"%s\".", username);
512 return NULL;
513 }
514
515 pass_hash = spwd->sp_pwdp;
516 } else {
517 pass_hash = pwd->pw_passwd;
518 }
519
520 if (!pass_hash) {
Michal Vaskod083db62016-01-19 10:31:29 +0100521 ERR("No password could be retrieved for \"%s\".", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100522 return NULL;
523 }
524
525 /* check the hash structure for special meaning */
526 if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) {
527 VRB("User \"%s\" is not allowed to authenticate using a password.", username);
528 return NULL;
529 }
530 if (!strcmp(pass_hash, "*NP*")) {
531 VRB("Retrieving password for \"%s\" from a NIS+ server not supported.", username);
532 return NULL;
533 }
534
535 return strdup(pass_hash);
536}
537
538static int
539auth_password_compare_pwd(const char *pass_hash, const char *pass_clear)
540{
541 char *new_pass_hash;
542 struct crypt_data cdata;
543
544 if (!pass_hash[0]) {
545 if (!pass_clear[0]) {
546 WRN("User authentication successful with an empty password!");
547 return 0;
548 } else {
549 /* the user did now know he does not need any password,
550 * (which should not be used) so deny authentication */
551 return 1;
552 }
553 }
554
555 cdata.initialized = 0;
556 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
557 return strcmp(new_pass_hash, pass_hash);
558}
559
560static void
561nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
562{
563 char *pass_hash;
564
565 pass_hash = auth_password_get_pwd_hash(session->username);
566 if (pass_hash && !auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100567 VRB("User \"%s\" authenticated.", session->username);
Michal Vasko086311b2016-01-08 09:53:11 +0100568 ssh_message_auth_reply_success(msg, 0);
569 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
570 free(pass_hash);
571 return;
572 }
573
574 free(pass_hash);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100575 ++session->ssh_auth_attempts;
Michal Vasko296fee82016-05-04 08:57:31 +0200576 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100577 ssh_message_reply_default(msg);
578}
579
580static void
581nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
582{
583 char *pass_hash;
584
585 if (!ssh_message_auth_kbdint_is_response(msg)) {
586 const char *prompts[] = {"Password: "};
587 char echo[] = {0};
588
589 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
590 } else {
591 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {
592 ssh_message_reply_default(msg);
593 return;
594 }
595 pass_hash = auth_password_get_pwd_hash(session->username);
596 if (!pass_hash) {
597 ssh_message_reply_default(msg);
598 return;
599 }
600 if (!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0))) {
601 VRB("User \"%s\" authenticated.", session->username);
602 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
603 ssh_message_auth_reply_success(msg, 0);
604 } else {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100605 ++session->ssh_auth_attempts;
606 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100607 ssh_message_reply_default(msg);
608 }
Radek Krejcifb533742016-03-04 15:12:54 +0100609 free(pass_hash);
Michal Vasko086311b2016-01-08 09:53:11 +0100610 }
611}
612
613static const char *
Michal Vasko3031aae2016-01-27 16:07:18 +0100614auth_pubkey_compare_key(struct nc_server_ssh_opts *opts, ssh_key key)
Michal Vasko086311b2016-01-08 09:53:11 +0100615{
616 uint32_t i;
617 ssh_key pub_key;
Michal Vasko76e3a352016-01-18 09:07:00 +0100618 const char *username = NULL;
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200619 int ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100620
Michal Vasko3031aae2016-01-27 16:07:18 +0100621 for (i = 0; i < opts->authkey_count; ++i) {
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200622 ret = ssh_pki_import_pubkey_file(opts->authkeys[i].path, &pub_key);
623 if (ret == SSH_EOF) {
624 WRN("Failed to import the public key \"%s\" (File access problem).", opts->authkeys[i].path);
625 continue;
626 } else if (ret == SSH_ERROR) {
627 WRN("Failed to import the public key \"%s\" (SSH error).", opts->authkeys[i].path);
Michal Vasko086311b2016-01-08 09:53:11 +0100628 continue;
629 }
630
631 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
632 ssh_key_free(pub_key);
633 break;
634 }
635
636 ssh_key_free(pub_key);
637 }
638
Michal Vasko3031aae2016-01-27 16:07:18 +0100639 if (i < opts->authkey_count) {
640 username = opts->authkeys[i].username;
Michal Vasko086311b2016-01-08 09:53:11 +0100641 }
642
643 return username;
644}
645
646static void
647nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
648{
649 const char *username;
650 int signature_state;
651
Michal Vaskobd13a932016-09-14 09:00:35 +0200652 if ((username = auth_pubkey_compare_key(session->data, ssh_message_auth_pubkey(msg))) == NULL) {
653 VRB("User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
654 goto fail;
655 } else if (strcmp(session->username, username)) {
656 VRB("User \"%s\" is not the username identified with the presented public key.", session->username);
Michal Vaskobd13a932016-09-14 09:00:35 +0200657 goto fail;
658 }
Michal Vaskobd13a932016-09-14 09:00:35 +0200659
Michal Vasko086311b2016-01-08 09:53:11 +0100660 signature_state = ssh_message_auth_publickey_state(msg);
661 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
662 VRB("User \"%s\" authenticated.", session->username);
663 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
664 ssh_message_auth_reply_success(msg, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100665 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Michal Vaskobd13a932016-09-14 09:00:35 +0200666 /* accepting only the use of a public key */
667 ssh_message_auth_reply_pk_ok_simple(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100668 }
669
Michal Vaskobd13a932016-09-14 09:00:35 +0200670 return;
671
672fail:
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100673 ++session->ssh_auth_attempts;
674 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100675 ssh_message_reply_default(msg);
676}
677
678static int
Michal Vasko96164bf2016-01-21 15:41:58 +0100679nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100680{
Michal Vasko96164bf2016-01-21 15:41:58 +0100681 ssh_channel chan;
682
683 /* first channel request */
684 if (!session->ti.libssh.channel) {
685 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100686 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100687 return -1;
688 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100689 chan = ssh_message_channel_request_open_reply_accept(msg);
690 if (!chan) {
691 ERR("Failed to create a new SSH channel.");
692 return -1;
693 }
694 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +0100695
Michal Vasko96164bf2016-01-21 15:41:58 +0100696 /* additional channel request */
697 } else {
698 chan = ssh_message_channel_request_open_reply_accept(msg);
699 if (!chan) {
700 ERR("Session %u: failed to create a new SSH channel.", session->id);
701 return -1;
702 }
703 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +0100704 }
705
Michal Vasko086311b2016-01-08 09:53:11 +0100706 return 0;
707}
708
709static int
710nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
711{
Michal Vasko96164bf2016-01-21 15:41:58 +0100712 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +0100713
Michal Vasko96164bf2016-01-21 15:41:58 +0100714 if (strcmp(subsystem, "netconf")) {
715 WRN("Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +0100716 return -1;
717 }
718
Michal Vasko96164bf2016-01-21 15:41:58 +0100719 if (session->ti.libssh.channel == channel) {
720 /* first channel requested */
721 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
722 ERRINT;
723 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100724 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100725 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
726 ERR("Session %u: subsystem \"netconf\" requested for the second time.", session->id);
727 return -1;
728 }
729
730 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +0100731 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +0100732 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
733 new_session = calloc(1, sizeof *new_session);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100734 if (!new_session) {
735 ERRMEM;
736 return -1;
737 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100738
739 /* insert the new session */
740 if (!session->ti.libssh.next) {
741 new_session->ti.libssh.next = session;
742 } else {
743 new_session->ti.libssh.next = session->ti.libssh.next;
744 }
745 session->ti.libssh.next = new_session;
746
747 new_session->status = NC_STATUS_STARTING;
748 new_session->side = NC_SERVER;
749 new_session->ti_type = NC_TI_LIBSSH;
750 new_session->ti_lock = session->ti_lock;
751 new_session->ti.libssh.channel = channel;
752 new_session->ti.libssh.session = session->ti.libssh.session;
753 new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
754 new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
755 new_session->port = session->port;
756 new_session->ctx = server_opts.ctx;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100757 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX
758 | (session->flags & NC_SESSION_CALLHOME ? NC_SESSION_CALLHOME : 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100759 }
760
761 return 0;
762}
763
Michal Vasko96164bf2016-01-21 15:41:58 +0100764int
Michal Vaskob48aa812016-01-18 14:13:09 +0100765nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +0100766{
767 const char *str_type, *str_subtype = NULL, *username;
768 int subtype, type;
769 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +0100770
771 type = ssh_message_type(msg);
772 subtype = ssh_message_subtype(msg);
773
774 switch (type) {
775 case SSH_REQUEST_AUTH:
776 str_type = "request-auth";
777 switch (subtype) {
778 case SSH_AUTH_METHOD_NONE:
779 str_subtype = "none";
780 break;
781 case SSH_AUTH_METHOD_PASSWORD:
782 str_subtype = "password";
783 break;
784 case SSH_AUTH_METHOD_PUBLICKEY:
785 str_subtype = "publickey";
786 break;
787 case SSH_AUTH_METHOD_HOSTBASED:
788 str_subtype = "hostbased";
789 break;
790 case SSH_AUTH_METHOD_INTERACTIVE:
791 str_subtype = "interactive";
792 break;
793 case SSH_AUTH_METHOD_GSSAPI_MIC:
794 str_subtype = "gssapi-mic";
795 break;
796 }
797 break;
798
799 case SSH_REQUEST_CHANNEL_OPEN:
800 str_type = "request-channel-open";
801 switch (subtype) {
802 case SSH_CHANNEL_SESSION:
803 str_subtype = "session";
804 break;
805 case SSH_CHANNEL_DIRECT_TCPIP:
806 str_subtype = "direct-tcpip";
807 break;
808 case SSH_CHANNEL_FORWARDED_TCPIP:
809 str_subtype = "forwarded-tcpip";
810 break;
811 case (int)SSH_CHANNEL_X11:
812 str_subtype = "channel-x11";
813 break;
814 case SSH_CHANNEL_UNKNOWN:
815 /* fallthrough */
816 default:
817 str_subtype = "unknown";
818 break;
819 }
820 break;
821
822 case SSH_REQUEST_CHANNEL:
823 str_type = "request-channel";
824 switch (subtype) {
825 case SSH_CHANNEL_REQUEST_PTY:
826 str_subtype = "pty";
827 break;
828 case SSH_CHANNEL_REQUEST_EXEC:
829 str_subtype = "exec";
830 break;
831 case SSH_CHANNEL_REQUEST_SHELL:
832 str_subtype = "shell";
833 break;
834 case SSH_CHANNEL_REQUEST_ENV:
835 str_subtype = "env";
836 break;
837 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
838 str_subtype = "subsystem";
839 break;
840 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
841 str_subtype = "window-change";
842 break;
843 case SSH_CHANNEL_REQUEST_X11:
844 str_subtype = "x11";
845 break;
846 case SSH_CHANNEL_REQUEST_UNKNOWN:
847 /* fallthrough */
848 default:
849 str_subtype = "unknown";
850 break;
851 }
852 break;
853
854 case SSH_REQUEST_SERVICE:
855 str_type = "request-service";
856 str_subtype = ssh_message_service_service(msg);
857 break;
858
859 case SSH_REQUEST_GLOBAL:
860 str_type = "request-global";
861 switch (subtype) {
862 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
863 str_subtype = "tcpip-forward";
864 break;
865 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
866 str_subtype = "cancel-tcpip-forward";
867 break;
868 case SSH_GLOBAL_REQUEST_UNKNOWN:
869 /* fallthrough */
870 default:
871 str_subtype = "unknown";
872 break;
873 }
874 break;
875
876 default:
877 str_type = "unknown";
878 str_subtype = "unknown";
879 break;
880 }
881
882 VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vaskoce319162016-02-03 15:33:08 +0100883 if ((session->status == NC_STATUS_CLOSING) || (session->status == NC_STATUS_INVALID)) {
884 /* "valid" situation if, for example, receiving some auth or channel request timeouted,
885 * but we got it now, during session free */
886 VRB("SSH message arrived on a %s session, the request will be denied.",
887 (session->status == NC_STATUS_CLOSING ? "closing" : "invalid"));
888 ssh_message_reply_default(msg);
889 return 0;
890 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100891 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +0100892
893 /*
894 * process known messages
895 */
896 if (type == SSH_REQUEST_AUTH) {
897 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
898 ERR("User \"%s\" authenticated, but requested another authentication.", session->username);
899 ssh_message_reply_default(msg);
900 return 0;
901 }
902
Michal Vasko086311b2016-01-08 09:53:11 +0100903 /* save the username, do not let the client change it */
904 username = ssh_message_auth_user(msg);
905 if (!session->username) {
906 if (!username) {
907 ERR("Denying an auth request without a username.");
908 return 1;
909 }
910
Michal Vasko05ba9df2016-01-13 14:40:27 +0100911 session->username = lydict_insert(server_opts.ctx, username, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100912 } else if (username) {
913 if (strcmp(username, session->username)) {
914 ERR("User \"%s\" changed its username to \"%s\".", session->username, username);
915 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +0100916 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +0100917 return 1;
918 }
919 }
920
921 if (subtype == SSH_AUTH_METHOD_NONE) {
922 /* libssh will return the supported auth methods */
923 return 1;
924 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
925 nc_sshcb_auth_password(session, msg);
926 return 0;
927 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
928 nc_sshcb_auth_pubkey(session, msg);
929 return 0;
930 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
931 nc_sshcb_auth_kbdint(session, msg);
932 return 0;
933 }
934 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +0100935 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100936 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100937 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100938 }
Michal Vasko086311b2016-01-08 09:53:11 +0100939 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +0100940
Michal Vasko0df67562016-01-21 15:50:11 +0100941 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100942 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
943 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +0100944 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +0100945 } else {
946 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100947 }
948 return 0;
949 }
950 }
951
952 /* we did not process it */
953 return 1;
954}
955
Michal Vasko1a38c862016-01-15 15:50:07 +0100956/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +0100957static int
958nc_open_netconf_channel(struct nc_session *session, int timeout)
959{
Michal Vasko62be1ce2016-03-03 13:24:52 +0100960 int elapsed_usec = 0, ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100961
962 /* message callback is executed twice to give chance for the channel to be
963 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100964 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +0100965 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100966 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +0100967 return -1;
968 }
969
Michal vasko50cc94f2016-10-04 13:46:20 +0200970 ret = nc_timedlock(session->ti_lock, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100971 if (ret != 1) {
972 return ret;
973 }
974
975 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
976 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100977 ERR("Failed to receive SSH messages on a session (%s).",
978 ssh_get_error(session->ti.libssh.session));
Michal Vasko11d142a2016-01-19 15:58:24 +0100979 pthread_mutex_unlock(session->ti_lock);
Michal Vasko086311b2016-01-08 09:53:11 +0100980 return -1;
981 }
982
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100983 if (!session->ti.libssh.channel) {
984 /* we did not receive channel-open, timeout */
985 pthread_mutex_unlock(session->ti_lock);
986 return 0;
987 }
988
989 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
990 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100991 ERR("Failed to receive SSH messages on a session (%s).",
992 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100993 pthread_mutex_unlock(session->ti_lock);
994 return -1;
995 }
996 pthread_mutex_unlock(session->ti_lock);
997
998 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
999 /* we did not receive subsystem-request, timeout */
1000 return 0;
1001 }
1002
1003 return 1;
1004 }
1005
1006 while (1) {
1007 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001008 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001009 return -1;
1010 }
1011
Michal vasko50cc94f2016-10-04 13:46:20 +02001012 ret = nc_timedlock(session->ti_lock, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001013 if (ret != 1) {
1014 return ret;
1015 }
1016
1017 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1018 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001019 ERR("Failed to receive SSH messages on a session (%s).",
1020 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001021 pthread_mutex_unlock(session->ti_lock);
1022 return -1;
1023 }
1024
1025 pthread_mutex_unlock(session->ti_lock);
1026
Michal Vasko086311b2016-01-08 09:53:11 +01001027 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001028 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001029 }
1030
Michal Vasko105bf272016-02-03 15:34:35 +01001031 if ((timeout != -1) && (elapsed_usec / 1000 >= timeout)) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001032 /* timeout */
Michal Vasko842522c2016-03-01 09:20:13 +01001033 ERR("Failed to start \"netconf\" SSH subsystem for too long, disconnecting.");
Michal Vasko086311b2016-01-08 09:53:11 +01001034 break;
1035 }
1036
Michal Vasko086311b2016-01-08 09:53:11 +01001037 usleep(NC_TIMEOUT_STEP);
Michal Vasko105bf272016-02-03 15:34:35 +01001038 elapsed_usec += NC_TIMEOUT_STEP;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001039 }
Michal Vasko086311b2016-01-08 09:53:11 +01001040
Michal Vasko1a38c862016-01-15 15:50:07 +01001041 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001042}
1043
Michal Vasko71090fc2016-05-24 16:37:28 +02001044API NC_MSG_TYPE
Michal Vasko8f5270d2016-02-29 16:22:25 +01001045nc_connect_callhome_ssh(const char *host, uint16_t port, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001046{
Michal Vasko8f5270d2016-02-29 16:22:25 +01001047 return nc_connect_callhome(host, port, NC_TI_LIBSSH, session);
Michal Vasko3031aae2016-01-27 16:07:18 +01001048}
1049
1050int
Michal Vasko0190bc32016-03-02 15:47:49 +01001051nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko3031aae2016-01-27 16:07:18 +01001052{
Michal Vaskoe2713da2016-08-22 16:06:40 +02001053 ssh_bind sbind;
Michal Vasko3031aae2016-01-27 16:07:18 +01001054 struct nc_server_ssh_opts *opts;
Michal Vasko72387da2016-02-02 15:52:41 +01001055 int libssh_auth_methods = 0, elapsed_usec = 0, ret;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001056 uint8_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001057
Michal Vasko2cc4c682016-03-01 09:16:48 +01001058 opts = session->data;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001059
Michal Vasko086311b2016-01-08 09:53:11 +01001060 /* other transport-specific data */
1061 session->ti_type = NC_TI_LIBSSH;
1062 session->ti.libssh.session = ssh_new();
1063 if (!session->ti.libssh.session) {
Michal Vaskod083db62016-01-19 10:31:29 +01001064 ERR("Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001065 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001066 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001067 }
1068
Michal Vaskoc61c4492016-01-25 11:13:34 +01001069 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001070 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1071 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001072 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001073 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1074 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001075 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001076 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1077 }
1078 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1079
Michal Vaskoe2713da2016-08-22 16:06:40 +02001080 sbind = ssh_bind_new();
1081 if (!sbind) {
1082 ERR("Failed to create an SSH bind.");
1083 close(sock);
1084 return -1;
1085 }
1086 for (i = 0; i < opts->hostkey_count; ++i) {
1087 if (ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_HOSTKEY, opts->hostkeys[i]) != SSH_OK) {
1088 ERR("Failed to set hostkey \"%s\" (%s).", opts->hostkeys[i], ssh_get_error(sbind));
1089 close(sock);
1090 ssh_bind_free(sbind);
1091 return -1;
1092 }
1093 }
1094 if (opts->banner) {
1095 ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_BANNER, opts->banner);
1096 }
1097
Michal Vasko086311b2016-01-08 09:53:11 +01001098 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001099 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001100 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001101
Michal Vaskoe2713da2016-08-22 16:06:40 +02001102 if (ssh_bind_accept_fd(sbind, session->ti.libssh.session, sock) == SSH_ERROR) {
1103 ERR("SSH failed to accept a new connection (%s).", ssh_get_error(sbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001104 close(sock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001105 ssh_bind_free(sbind);
Michal Vasko9e036d52016-01-08 10:49:26 +01001106 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001107 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001108 ssh_bind_free(sbind);
Michal Vasko086311b2016-01-08 09:53:11 +01001109
Michal Vasko0190bc32016-03-02 15:47:49 +01001110 ssh_set_blocking(session->ti.libssh.session, 0);
1111
1112 while ((ret = ssh_handle_key_exchange(session->ti.libssh.session)) == SSH_AGAIN) {
Michal Vaskoe65b4492016-03-03 11:57:51 +01001113 /* this tends to take longer */
1114 usleep(NC_TIMEOUT_STEP * 20);
1115 elapsed_usec += NC_TIMEOUT_STEP * 20;
Michal Vasko0190bc32016-03-02 15:47:49 +01001116 if ((timeout > -1) && (elapsed_usec / 1000 >= timeout)) {
1117 break;
1118 }
1119 }
1120 if (ret == SSH_AGAIN) {
1121 ERR("SSH key exchange timeout.");
1122 return 0;
1123 } else if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001124 ERR("SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001125 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001126 }
1127
1128 /* authenticate */
Michal Vasko0190bc32016-03-02 15:47:49 +01001129 elapsed_usec = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001130 do {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001131 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001132 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001133 return -1;
1134 }
1135
Michal Vasko086311b2016-01-08 09:53:11 +01001136 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001137 ERR("Failed to receive SSH messages on a session (%s).",
1138 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001139 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001140 }
1141
Michal Vaskof7a99be2017-02-07 10:57:27 +01001142 if (session->opts.server.ssh_auth_attempts >= opts->auth_attempts) {
1143 ERR("Too many failed authentication attempts of user \"%s\".", session->username);
1144 return -1;
1145 }
1146
Michal Vasko086311b2016-01-08 09:53:11 +01001147 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1148 break;
1149 }
1150
1151 usleep(NC_TIMEOUT_STEP);
Michal Vasko72387da2016-02-02 15:52:41 +01001152 elapsed_usec += NC_TIMEOUT_STEP;
Michal Vaskoab639942016-02-29 16:23:47 +01001153 } while (!opts->auth_timeout || (elapsed_usec / 1000000 < opts->auth_timeout));
Michal Vasko086311b2016-01-08 09:53:11 +01001154
1155 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1156 /* timeout */
Michal Vasko842522c2016-03-01 09:20:13 +01001157 ERR("Client failed to authenticate for too long, disconnecting.");
Michal Vasko1a38c862016-01-15 15:50:07 +01001158 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001159 }
1160
Michal Vasko086311b2016-01-08 09:53:11 +01001161 /* open channel */
Michal Vaskoab639942016-02-29 16:23:47 +01001162 ret = nc_open_netconf_channel(session, opts->auth_timeout ? (opts->auth_timeout * 1000 - elapsed_usec / 1000) : -1);
Michal Vasko1a38c862016-01-15 15:50:07 +01001163 if (ret < 1) {
1164 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001165 }
1166
Michal Vasko96164bf2016-01-21 15:41:58 +01001167 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
Michal Vasko1a38c862016-01-15 15:50:07 +01001168 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001169}
1170
Michal Vasko71090fc2016-05-24 16:37:28 +02001171API NC_MSG_TYPE
1172nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session **session)
1173{
1174 NC_MSG_TYPE msgtype;
1175 struct nc_session *new_session = NULL;
1176
1177 if (!orig_session) {
1178 ERRARG("orig_session");
1179 return NC_MSG_ERROR;
1180 } else if (!session) {
1181 ERRARG("session");
1182 return NC_MSG_ERROR;
1183 }
1184
1185 if ((orig_session->status == NC_STATUS_RUNNING) && (orig_session->ti_type == NC_TI_LIBSSH)
1186 && orig_session->ti.libssh.next) {
1187 for (new_session = orig_session->ti.libssh.next;
1188 new_session != orig_session;
1189 new_session = new_session->ti.libssh.next) {
1190 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1191 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1192 /* we found our session */
1193 break;
1194 }
1195 }
1196 if (new_session == orig_session) {
1197 new_session = NULL;
1198 }
1199 }
1200
1201 if (!new_session) {
1202 ERR("Session does not have a NETCONF SSH channel ready.");
1203 return NC_MSG_ERROR;
1204 }
1205
1206 /* assign new SID atomically */
1207 pthread_spin_lock(&server_opts.sid_lock);
1208 new_session->id = server_opts.new_session_id++;
1209 pthread_spin_unlock(&server_opts.sid_lock);
1210
1211 /* NETCONF handshake */
1212 msgtype = nc_handshake(new_session);
1213 if (msgtype != NC_MSG_HELLO) {
1214 return msgtype;
1215 }
1216
Michal Vasko2f975cf2016-07-28 15:47:00 +02001217 new_session->session_start = new_session->last_rpc = time(NULL);
Michal Vasko71090fc2016-05-24 16:37:28 +02001218 new_session->status = NC_STATUS_RUNNING;
1219 *session = new_session;
1220
1221 return msgtype;
1222}
1223
1224API NC_MSG_TYPE
Michal Vasko96164bf2016-01-21 15:41:58 +01001225nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001226{
Michal Vaskobdcf2362016-07-26 11:35:43 +02001227 uint8_t q_id;
Michal Vasko71090fc2016-05-24 16:37:28 +02001228 NC_MSG_TYPE msgtype;
Michal Vasko96164bf2016-01-21 15:41:58 +01001229 struct nc_session *new_session = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001230 uint16_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001231
Michal Vasko45e53ae2016-04-07 11:46:03 +02001232 if (!ps) {
1233 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001234 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001235 } else if (!session) {
1236 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001237 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01001238 }
1239
Michal Vasko48a63ed2016-03-01 09:48:21 +01001240 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001241 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001242 return NC_MSG_ERROR;
Michal Vaskof04a52a2016-04-07 10:52:10 +02001243 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001244
Michal Vasko96164bf2016-01-21 15:41:58 +01001245 for (i = 0; i < ps->session_count; ++i) {
1246 if ((ps->sessions[i]->status == NC_STATUS_RUNNING) && (ps->sessions[i]->ti_type == NC_TI_LIBSSH)
1247 && ps->sessions[i]->ti.libssh.next) {
1248 /* an SSH session with more channels */
1249 for (new_session = ps->sessions[i]->ti.libssh.next;
1250 new_session != ps->sessions[i];
1251 new_session = new_session->ti.libssh.next) {
1252 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1253 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1254 /* we found our session */
1255 break;
1256 }
1257 }
1258 if (new_session != ps->sessions[i]) {
1259 break;
1260 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001261
Michal Vasko96164bf2016-01-21 15:41:58 +01001262 new_session = NULL;
1263 }
1264 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001265
Michal Vasko48a63ed2016-03-01 09:48:21 +01001266 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001267 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001268
Michal Vasko96164bf2016-01-21 15:41:58 +01001269 if (!new_session) {
1270 ERR("No session with a NETCONF SSH channel ready was found.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001271 return NC_MSG_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001272 }
1273
1274 /* assign new SID atomically */
1275 pthread_spin_lock(&server_opts.sid_lock);
1276 new_session->id = server_opts.new_session_id++;
1277 pthread_spin_unlock(&server_opts.sid_lock);
Michal Vaskofb89d772016-01-08 12:25:35 +01001278
Michal Vasko086311b2016-01-08 09:53:11 +01001279 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001280 msgtype = nc_handshake(new_session);
1281 if (msgtype != NC_MSG_HELLO) {
1282 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001283 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001284
Michal Vasko2f975cf2016-07-28 15:47:00 +02001285 new_session->session_start = new_session->last_rpc = time(NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001286 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001287 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001288
Michal Vasko71090fc2016-05-24 16:37:28 +02001289 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001290}