blob: ed97691408669ad87dbd46ef6b4146f328866d17 [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
29extern struct nc_server_opts server_opts;
Michal Vaskob05053d2016-01-22 16:12:06 +010030
Michal Vaskob05053d2016-01-22 16:12:06 +010031static int
Michal Vaskoe2713da2016-08-22 16:06:40 +020032nc_server_ssh_add_hostkey(const char *privkey_path, struct nc_server_ssh_opts *opts)
Michal Vasko086311b2016-01-08 09:53:11 +010033{
Michal Vasko1a38c862016-01-15 15:50:07 +010034 if (!privkey_path) {
Michal Vasko45e53ae2016-04-07 11:46:03 +020035 ERRARG("privkey_path");
Michal Vasko086311b2016-01-08 09:53:11 +010036 return -1;
37 }
38
Michal Vaskoe2713da2016-08-22 16:06:40 +020039 if (eaccess(privkey_path, R_OK)) {
40 ERR("Host key \"%s\" cannot be read (%s).", privkey_path, strerror(errno));
Michal Vasko5fcc7142016-02-02 12:21:10 +010041 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +010042 }
Michal Vaskod45e25a2016-01-08 15:48:44 +010043
Michal Vaskoe2713da2016-08-22 16:06:40 +020044 ++opts->hostkey_count;
45 opts->hostkeys = nc_realloc(opts->hostkeys, opts->hostkey_count * sizeof *opts->hostkeys);
46 if (!opts->hostkeys) {
47 ERRMEM;
48 return -1;
49 }
50 opts->hostkeys[opts->hostkey_count - 1] = lydict_insert(server_opts.ctx, privkey_path, 0);
51
Michal Vasko5fcc7142016-02-02 12:21:10 +010052 return 0;
Michal Vaskob05053d2016-01-22 16:12:06 +010053}
54
55API int
Michal Vaskoe2713da2016-08-22 16:06:40 +020056nc_server_ssh_endpt_add_hostkey(const char *endpt_name, const char *privkey_path)
Michal Vaskob05053d2016-01-22 16:12:06 +010057{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010058 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +010059 struct nc_endpt *endpt;
60
Michal Vasko51e514d2016-02-02 15:51:52 +010061 /* LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020062 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +010063 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +010064 return -1;
65 }
Michal Vasko2e6defd2016-10-07 15:48:15 +020066 ret = nc_server_ssh_add_hostkey(privkey_path, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +010067 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010068 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +010069
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010070 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +010071}
72
73API int
Michal Vasko2e6defd2016-10-07 15:48:15 +020074nc_server_ssh_ch_client_add_hostkey(const char *client_name, const char *privkey_path)
Michal Vaskob05053d2016-01-22 16:12:06 +010075{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010076 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +020077 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010078
Michal Vasko2e6defd2016-10-07 15:48:15 +020079 /* LOCK */
80 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
81 if (!client) {
82 return -1;
83 }
84 ret = nc_server_ssh_add_hostkey(privkey_path, client->opts.ssh);
85 /* UNLOCK */
86 nc_server_ch_client_unlock(client);
Michal Vaskoe2713da2016-08-22 16:06:40 +020087
88 return ret;
89}
90
91static int
92nc_server_ssh_del_hostkey(const char *privkey_path, struct nc_server_ssh_opts *opts)
93{
94 uint8_t i;
95
96 if (!privkey_path) {
97 for (i = 0; i < opts->hostkey_count; ++i) {
98 lydict_remove(server_opts.ctx, opts->hostkeys[i]);
99 }
100 free(opts->hostkeys);
101 opts->hostkeys = NULL;
102 opts->hostkey_count = 0;
103 } else {
104 for (i = 0; i < opts->hostkey_count; ++i) {
105 if (!strcmp(opts->hostkeys[i], privkey_path)) {
106 --opts->hostkey_count;
107 lydict_remove(server_opts.ctx, opts->hostkeys[i]);
108 if (i < opts->hostkey_count - 1) {
109 memmove(opts->hostkeys + i, opts->hostkeys + i + 1, (opts->hostkey_count - i) * sizeof *opts->hostkeys);
110 }
111 return 0;
112 }
113 }
114
115 ERR("Host key \"%s\" not found.", privkey_path);
116 return -1;
117 }
118
119 return 0;
120}
121
122API int
123nc_server_ssh_endpt_del_hostkey(const char *endpt_name, const char *privkey_path)
124{
125 int ret;
126 struct nc_endpt *endpt;
127
128 /* LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200129 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200130 if (!endpt) {
131 return -1;
132 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200133 ret = nc_server_ssh_del_hostkey(privkey_path, endpt->opts.ssh);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200134 /* UNLOCK */
135 nc_server_endpt_unlock(endpt);
136
137 return ret;
138}
139
140API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200141nc_server_ssh_ch_client_del_hostkey(const char *client_name, const char *privkey_path)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200142{
143 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200144 struct nc_ch_client *client;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200145
Michal Vasko2e6defd2016-10-07 15:48:15 +0200146 /* LOCK */
147 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
148 if (!client) {
149 return -1;
150 }
151 ret = nc_server_ssh_del_hostkey(privkey_path, client->opts.ssh);
152 /* UNLOCK */
153 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100154
155 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100156}
157
158static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100159nc_server_ssh_set_banner(const char *banner, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100160{
Michal Vaskob05053d2016-01-22 16:12:06 +0100161 if (!banner) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200162 ERRARG("banner");
Michal Vaskob05053d2016-01-22 16:12:06 +0100163 return -1;
164 }
165
Michal Vaskoe2713da2016-08-22 16:06:40 +0200166 if (opts->banner) {
167 lydict_remove(server_opts.ctx, opts->banner);
Michal Vaskob05053d2016-01-22 16:12:06 +0100168 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200169 opts->banner = lydict_insert(server_opts.ctx, banner, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +0100170 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100171}
172
173API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100174nc_server_ssh_endpt_set_banner(const char *endpt_name, const char *banner)
Michal Vasko086311b2016-01-08 09:53:11 +0100175{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100176 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100177 struct nc_endpt *endpt;
178
Michal Vasko51e514d2016-02-02 15:51:52 +0100179 /* LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200180 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100181 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100182 return -1;
183 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200184 ret = nc_server_ssh_set_banner(banner, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100185 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100186 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100187
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100188 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100189}
190
191API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200192nc_server_ssh_ch_client_set_banner(const char *client_name, const char *banner)
Michal Vasko086311b2016-01-08 09:53:11 +0100193{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100194 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200195 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100196
Michal Vasko2e6defd2016-10-07 15:48:15 +0200197 /* LOCK */
198 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
199 if (!client) {
200 return -1;
201 }
202 ret = nc_server_ssh_set_banner(banner, client->opts.ssh);
203 /* UNLOCK */
204 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100205
206 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100207}
208
209static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100210nc_server_ssh_set_auth_methods(int auth_methods, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100211{
Michal Vasko086311b2016-01-08 09:53:11 +0100212 if (!(auth_methods & NC_SSH_AUTH_PUBLICKEY) && !(auth_methods & NC_SSH_AUTH_PASSWORD)
213 && !(auth_methods & NC_SSH_AUTH_INTERACTIVE)) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200214 ERRARG("auth_methods");
Michal Vasko086311b2016-01-08 09:53:11 +0100215 return -1;
216 }
217
Michal Vaskob05053d2016-01-22 16:12:06 +0100218 opts->auth_methods = auth_methods;
219 return 0;
220}
221
222API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100223nc_server_ssh_endpt_set_auth_methods(const char *endpt_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100224{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100225 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100226 struct nc_endpt *endpt;
227
Michal Vasko51e514d2016-02-02 15:51:52 +0100228 /* LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200229 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100230 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100231 return -1;
232 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200233 ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100234 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100235 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100236
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100237 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100238}
239
240API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200241nc_server_ssh_ch_client_set_auth_methods(const char *client_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100242{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100243 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200244 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100245
Michal Vasko2e6defd2016-10-07 15:48:15 +0200246 /* LOCK */
247 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
248 if (!client) {
249 return -1;
250 }
251 ret = nc_server_ssh_set_auth_methods(auth_methods, client->opts.ssh);
252 /* UNLOCK */
253 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100254
255 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100256}
257
258static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100259nc_server_ssh_set_auth_attempts(uint16_t auth_attempts, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100260{
Michal Vaskob05053d2016-01-22 16:12:06 +0100261 if (!auth_attempts) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200262 ERRARG("auth_attempts");
Michal Vaskob05053d2016-01-22 16:12:06 +0100263 return -1;
264 }
265
Michal Vaskob05053d2016-01-22 16:12:06 +0100266 opts->auth_attempts = auth_attempts;
Michal Vasko086311b2016-01-08 09:53:11 +0100267 return 0;
268}
269
270API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100271nc_server_ssh_endpt_set_auth_attempts(const char *endpt_name, uint16_t auth_attempts)
Michal Vasko086311b2016-01-08 09:53:11 +0100272{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100273 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100274 struct nc_endpt *endpt;
275
Michal Vasko51e514d2016-02-02 15:51:52 +0100276 /* LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200277 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100278 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100279 return -1;
280 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200281 ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100282 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100283 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100284
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100285 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100286}
287
288API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200289nc_server_ssh_set_ch_client_auth_attempts(const char *client_name, uint16_t auth_attempts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100290{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100291 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200292 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100293
Michal Vasko2e6defd2016-10-07 15:48:15 +0200294 /* LOCK */
295 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
296 if (!client) {
297 return -1;
298 }
299 ret = nc_server_ssh_set_auth_attempts(auth_attempts, client->opts.ssh);
300 /* UNLOCK */
301 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100302
303 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100304}
305
306static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100307nc_server_ssh_set_auth_timeout(uint16_t auth_timeout, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100308{
Michal Vaskob05053d2016-01-22 16:12:06 +0100309 if (!auth_timeout) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200310 ERRARG("auth_timeout");
Michal Vasko086311b2016-01-08 09:53:11 +0100311 return -1;
312 }
313
Michal Vaskob05053d2016-01-22 16:12:06 +0100314 opts->auth_timeout = auth_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100315 return 0;
316}
317
318API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100319nc_server_ssh_endpt_set_auth_timeout(const char *endpt_name, uint16_t auth_timeout)
Michal Vasko086311b2016-01-08 09:53:11 +0100320{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100321 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100322 struct nc_endpt *endpt;
323
Michal Vasko51e514d2016-02-02 15:51:52 +0100324 /* LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200325 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100326 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100327 return -1;
328 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200329 ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100330 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100331 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100332
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100333 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100334}
335
336API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200337nc_server_ssh_ch_client_set_auth_timeout(const char *client_name, uint16_t auth_timeout)
Michal Vaskob05053d2016-01-22 16:12:06 +0100338{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100339 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200340 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100341
Michal Vasko2e6defd2016-10-07 15:48:15 +0200342 /* LOCK */
343 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
344 if (!client) {
345 return -1;
346 }
347 ret = nc_server_ssh_set_auth_timeout(auth_timeout, client->opts.ssh);
348 /* UNLOCK */
349 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100350
351 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100352}
353
354static int
Michal Vasko17dfda92016-12-01 14:06:16 +0100355_nc_server_ssh_add_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
356 const char *username)
357{
358 ++server_opts.authkey_count;
359 server_opts.authkeys = nc_realloc(server_opts.authkeys, server_opts.authkey_count * sizeof *server_opts.authkeys);
360 if (!server_opts.authkeys) {
361 ERRMEM;
362 return -1;
363 }
364 server_opts.authkeys[server_opts.authkey_count - 1].path = lydict_insert(server_opts.ctx, pubkey_path, 0);
365 server_opts.authkeys[server_opts.authkey_count - 1].base64 = lydict_insert(server_opts.ctx, pubkey_base64, 0);
366 server_opts.authkeys[server_opts.authkey_count - 1].type = type;
367 server_opts.authkeys[server_opts.authkey_count - 1].username = lydict_insert(server_opts.ctx, username, 0);
368
369 return 0;
370}
371
372API int
373nc_server_ssh_add_authkey_path(const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100374{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200375 if (!pubkey_path) {
376 ERRARG("pubkey_path");
377 return -1;
378 } else if (!username) {
379 ERRARG("username");
Michal Vasko086311b2016-01-08 09:53:11 +0100380 return -1;
381 }
382
Michal Vasko17dfda92016-12-01 14:06:16 +0100383 return _nc_server_ssh_add_authkey(pubkey_path, NULL, 0, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100384}
385
386API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100387nc_server_ssh_add_authkey(const char *pubkey_base64, NC_SSH_KEY_TYPE type, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100388{
Michal Vasko17dfda92016-12-01 14:06:16 +0100389 if (!pubkey_base64) {
390 ERRARG("pubkey_base64");
391 return -1;
392 } else if (!type) {
393 ERRARG("type");
394 return -1;
395 } else if (!username) {
396 ERRARG("username");
Michal Vasko3031aae2016-01-27 16:07:18 +0100397 return -1;
398 }
399
Michal Vasko17dfda92016-12-01 14:06:16 +0100400 return _nc_server_ssh_add_authkey(NULL, pubkey_base64, type, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100401}
402
403API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100404nc_server_ssh_del_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
405 const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100406{
Michal Vasko086311b2016-01-08 09:53:11 +0100407 uint32_t i;
408 int ret = -1;
409
Michal Vasko17dfda92016-12-01 14:06:16 +0100410 /* LOCK */
411 pthread_mutex_lock(&server_opts.authkey_lock);
412
413 if (!pubkey_path && !pubkey_base64 && !type && !username) {
414 for (i = 0; i < server_opts.authkey_count; ++i) {
415 lydict_remove(server_opts.ctx, server_opts.authkeys[i].path);
416 lydict_remove(server_opts.ctx, server_opts.authkeys[i].base64);
417 lydict_remove(server_opts.ctx, server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100418
Michal Vasko086311b2016-01-08 09:53:11 +0100419 ret = 0;
420 }
Michal Vasko17dfda92016-12-01 14:06:16 +0100421 free(server_opts.authkeys);
422 server_opts.authkeys = NULL;
423 server_opts.authkey_count = 0;
Michal Vasko1a38c862016-01-15 15:50:07 +0100424 } else {
Michal Vasko17dfda92016-12-01 14:06:16 +0100425 for (i = 0; i < server_opts.authkey_count; ++i) {
426 if ((!pubkey_path || !strcmp(server_opts.authkeys[i].path, pubkey_path))
427 && (!pubkey_base64 || strcmp(server_opts.authkeys[i].base64, pubkey_base64))
428 && (!type || (server_opts.authkeys[i].type == type))
429 && (!username || !strcmp(server_opts.authkeys[i].username, username))) {
430 lydict_remove(server_opts.ctx, server_opts.authkeys[i].path);
431 lydict_remove(server_opts.ctx, server_opts.authkeys[i].base64);
432 lydict_remove(server_opts.ctx, server_opts.authkeys[i].username);
Michal Vasko1a38c862016-01-15 15:50:07 +0100433
Michal Vasko17dfda92016-12-01 14:06:16 +0100434 --server_opts.authkey_count;
435 if (i < server_opts.authkey_count) {
436 memcpy(&server_opts.authkeys[i], &server_opts.authkeys[server_opts.authkey_count],
437 sizeof *server_opts.authkeys);
438 } else if (!server_opts.authkey_count) {
439 free(server_opts.authkeys);
440 server_opts.authkeys = NULL;
Michal Vaskoc0256492016-02-02 12:19:06 +0100441 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100442
443 ret = 0;
444 }
445 }
Michal Vasko086311b2016-01-08 09:53:11 +0100446 }
447
Michal Vasko51e514d2016-02-02 15:51:52 +0100448 /* UNLOCK */
Michal Vasko17dfda92016-12-01 14:06:16 +0100449 pthread_mutex_unlock(&server_opts.authkey_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100450
451 return ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100452}
453
454void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100455nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100456{
Michal Vaskoe2713da2016-08-22 16:06:40 +0200457 nc_server_ssh_del_hostkey(NULL, opts);
458 if (opts->banner) {
459 lydict_remove(server_opts.ctx, opts->banner);
460 opts->banner = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +0100461 }
Michal Vaskob05053d2016-01-22 16:12:06 +0100462}
463
Michal Vasko086311b2016-01-08 09:53:11 +0100464static char *
465auth_password_get_pwd_hash(const char *username)
466{
467 struct passwd *pwd, pwd_buf;
468 struct spwd *spwd, spwd_buf;
469 char *pass_hash = NULL, buf[256];
470
471 getpwnam_r(username, &pwd_buf, buf, 256, &pwd);
472 if (!pwd) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100473 VRB("User \"%s\" not found locally.", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100474 return NULL;
475 }
476
477 if (!strcmp(pwd->pw_passwd, "x")) {
478 getspnam_r(username, &spwd_buf, buf, 256, &spwd);
479 if (!spwd) {
480 VRB("Failed to retrieve the shadow entry for \"%s\".", username);
481 return NULL;
482 }
483
484 pass_hash = spwd->sp_pwdp;
485 } else {
486 pass_hash = pwd->pw_passwd;
487 }
488
489 if (!pass_hash) {
Michal Vaskod083db62016-01-19 10:31:29 +0100490 ERR("No password could be retrieved for \"%s\".", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100491 return NULL;
492 }
493
494 /* check the hash structure for special meaning */
495 if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) {
496 VRB("User \"%s\" is not allowed to authenticate using a password.", username);
497 return NULL;
498 }
499 if (!strcmp(pass_hash, "*NP*")) {
500 VRB("Retrieving password for \"%s\" from a NIS+ server not supported.", username);
501 return NULL;
502 }
503
504 return strdup(pass_hash);
505}
506
507static int
508auth_password_compare_pwd(const char *pass_hash, const char *pass_clear)
509{
510 char *new_pass_hash;
511 struct crypt_data cdata;
512
513 if (!pass_hash[0]) {
514 if (!pass_clear[0]) {
515 WRN("User authentication successful with an empty password!");
516 return 0;
517 } else {
518 /* the user did now know he does not need any password,
519 * (which should not be used) so deny authentication */
520 return 1;
521 }
522 }
523
524 cdata.initialized = 0;
525 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
526 return strcmp(new_pass_hash, pass_hash);
527}
528
529static void
530nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
531{
532 char *pass_hash;
533
534 pass_hash = auth_password_get_pwd_hash(session->username);
535 if (pass_hash && !auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100536 VRB("User \"%s\" authenticated.", session->username);
Michal Vasko086311b2016-01-08 09:53:11 +0100537 ssh_message_auth_reply_success(msg, 0);
538 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
539 free(pass_hash);
540 return;
541 }
542
543 free(pass_hash);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200544 ++session->opts.server.ssh_auth_attempts;
545 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100546 ssh_message_reply_default(msg);
547}
548
549static void
550nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
551{
552 char *pass_hash;
553
554 if (!ssh_message_auth_kbdint_is_response(msg)) {
555 const char *prompts[] = {"Password: "};
556 char echo[] = {0};
557
558 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
559 } else {
560 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {
561 ssh_message_reply_default(msg);
562 return;
563 }
564 pass_hash = auth_password_get_pwd_hash(session->username);
565 if (!pass_hash) {
566 ssh_message_reply_default(msg);
567 return;
568 }
569 if (!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0))) {
570 VRB("User \"%s\" authenticated.", session->username);
571 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
572 ssh_message_auth_reply_success(msg, 0);
573 } else {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200574 ++session->opts.server.ssh_auth_attempts;
575 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100576 ssh_message_reply_default(msg);
577 }
Radek Krejcifb533742016-03-04 15:12:54 +0100578 free(pass_hash);
Michal Vasko086311b2016-01-08 09:53:11 +0100579 }
580}
581
582static const char *
Michal Vasko17dfda92016-12-01 14:06:16 +0100583auth_pubkey_compare_key(ssh_key key)
Michal Vasko086311b2016-01-08 09:53:11 +0100584{
585 uint32_t i;
586 ssh_key pub_key;
Michal Vasko76e3a352016-01-18 09:07:00 +0100587 const char *username = NULL;
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200588 int ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100589
Michal Vasko17dfda92016-12-01 14:06:16 +0100590 for (i = 0; i < server_opts.authkey_count; ++i) {
591 switch (server_opts.authkeys[i].type) {
592 case NC_SSH_KEY_UNKNOWN:
593 ret = ssh_pki_import_pubkey_file(server_opts.authkeys[i].path, &pub_key);
594 break;
595 case NC_SSH_KEY_DSA:
596 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_DSS, &pub_key);
597 break;
598 case NC_SSH_KEY_RSA:
599 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_RSA, &pub_key);
600 break;
601 case NC_SSH_KEY_ECDSA:
602 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_ECDSA, &pub_key);
603 break;
604 }
605
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200606 if (ret == SSH_EOF) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100607 WRN("Failed to import a public key of \"%s\" (File access problem).", server_opts.authkeys[i].username);
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200608 continue;
609 } else if (ret == SSH_ERROR) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100610 WRN("Failed to import a public key of \"%s\" (SSH error).", server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100611 continue;
612 }
613
614 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
615 ssh_key_free(pub_key);
616 break;
617 }
618
619 ssh_key_free(pub_key);
620 }
621
Michal Vasko17dfda92016-12-01 14:06:16 +0100622 if (i < server_opts.authkey_count) {
623 username = server_opts.authkeys[i].username;
Michal Vasko086311b2016-01-08 09:53:11 +0100624 }
625
626 return username;
627}
628
629static void
630nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
631{
632 const char *username;
633 int signature_state;
634
Michal Vasko17dfda92016-12-01 14:06:16 +0100635 if ((username = auth_pubkey_compare_key(ssh_message_auth_pubkey(msg))) == NULL) {
Michal Vaskobd13a932016-09-14 09:00:35 +0200636 VRB("User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
637 goto fail;
638 } else if (strcmp(session->username, username)) {
639 VRB("User \"%s\" is not the username identified with the presented public key.", session->username);
Michal Vaskobd13a932016-09-14 09:00:35 +0200640 goto fail;
641 }
Michal Vaskobd13a932016-09-14 09:00:35 +0200642
Michal Vasko086311b2016-01-08 09:53:11 +0100643 signature_state = ssh_message_auth_publickey_state(msg);
644 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
645 VRB("User \"%s\" authenticated.", session->username);
646 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
647 ssh_message_auth_reply_success(msg, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100648 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Michal Vaskobd13a932016-09-14 09:00:35 +0200649 /* accepting only the use of a public key */
650 ssh_message_auth_reply_pk_ok_simple(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100651 }
652
Michal Vaskobd13a932016-09-14 09:00:35 +0200653 return;
654
655fail:
Michal Vasko2e6defd2016-10-07 15:48:15 +0200656 ++session->opts.server.ssh_auth_attempts;
657 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100658 ssh_message_reply_default(msg);
659}
660
661static int
Michal Vasko96164bf2016-01-21 15:41:58 +0100662nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100663{
Michal Vasko96164bf2016-01-21 15:41:58 +0100664 ssh_channel chan;
665
666 /* first channel request */
667 if (!session->ti.libssh.channel) {
668 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100669 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100670 return -1;
671 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100672 chan = ssh_message_channel_request_open_reply_accept(msg);
673 if (!chan) {
674 ERR("Failed to create a new SSH channel.");
675 return -1;
676 }
677 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +0100678
Michal Vasko96164bf2016-01-21 15:41:58 +0100679 /* additional channel request */
680 } else {
681 chan = ssh_message_channel_request_open_reply_accept(msg);
682 if (!chan) {
683 ERR("Session %u: failed to create a new SSH channel.", session->id);
684 return -1;
685 }
686 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +0100687 }
688
Michal Vasko086311b2016-01-08 09:53:11 +0100689 return 0;
690}
691
692static int
693nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
694{
Michal Vasko96164bf2016-01-21 15:41:58 +0100695 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +0100696
Michal Vasko96164bf2016-01-21 15:41:58 +0100697 if (strcmp(subsystem, "netconf")) {
698 WRN("Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +0100699 return -1;
700 }
701
Michal Vasko96164bf2016-01-21 15:41:58 +0100702 if (session->ti.libssh.channel == channel) {
703 /* first channel requested */
704 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
705 ERRINT;
706 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100707 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100708 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
709 ERR("Session %u: subsystem \"netconf\" requested for the second time.", session->id);
710 return -1;
711 }
712
713 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +0100714 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +0100715 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
716 new_session = calloc(1, sizeof *new_session);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100717 if (!new_session) {
718 ERRMEM;
719 return -1;
720 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100721
722 /* insert the new session */
723 if (!session->ti.libssh.next) {
724 new_session->ti.libssh.next = session;
725 } else {
726 new_session->ti.libssh.next = session->ti.libssh.next;
727 }
728 session->ti.libssh.next = new_session;
729
730 new_session->status = NC_STATUS_STARTING;
731 new_session->side = NC_SERVER;
732 new_session->ti_type = NC_TI_LIBSSH;
733 new_session->ti_lock = session->ti_lock;
734 new_session->ti.libssh.channel = channel;
735 new_session->ti.libssh.session = session->ti.libssh.session;
736 new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
737 new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
738 new_session->port = session->port;
739 new_session->ctx = server_opts.ctx;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100740 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX
741 | (session->flags & NC_SESSION_CALLHOME ? NC_SESSION_CALLHOME : 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100742 }
743
744 return 0;
745}
746
Michal Vasko96164bf2016-01-21 15:41:58 +0100747int
Michal Vaskob48aa812016-01-18 14:13:09 +0100748nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +0100749{
750 const char *str_type, *str_subtype = NULL, *username;
751 int subtype, type;
752 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +0100753
754 type = ssh_message_type(msg);
755 subtype = ssh_message_subtype(msg);
756
757 switch (type) {
758 case SSH_REQUEST_AUTH:
759 str_type = "request-auth";
760 switch (subtype) {
761 case SSH_AUTH_METHOD_NONE:
762 str_subtype = "none";
763 break;
764 case SSH_AUTH_METHOD_PASSWORD:
765 str_subtype = "password";
766 break;
767 case SSH_AUTH_METHOD_PUBLICKEY:
768 str_subtype = "publickey";
769 break;
770 case SSH_AUTH_METHOD_HOSTBASED:
771 str_subtype = "hostbased";
772 break;
773 case SSH_AUTH_METHOD_INTERACTIVE:
774 str_subtype = "interactive";
775 break;
776 case SSH_AUTH_METHOD_GSSAPI_MIC:
777 str_subtype = "gssapi-mic";
778 break;
779 }
780 break;
781
782 case SSH_REQUEST_CHANNEL_OPEN:
783 str_type = "request-channel-open";
784 switch (subtype) {
785 case SSH_CHANNEL_SESSION:
786 str_subtype = "session";
787 break;
788 case SSH_CHANNEL_DIRECT_TCPIP:
789 str_subtype = "direct-tcpip";
790 break;
791 case SSH_CHANNEL_FORWARDED_TCPIP:
792 str_subtype = "forwarded-tcpip";
793 break;
794 case (int)SSH_CHANNEL_X11:
795 str_subtype = "channel-x11";
796 break;
797 case SSH_CHANNEL_UNKNOWN:
798 /* fallthrough */
799 default:
800 str_subtype = "unknown";
801 break;
802 }
803 break;
804
805 case SSH_REQUEST_CHANNEL:
806 str_type = "request-channel";
807 switch (subtype) {
808 case SSH_CHANNEL_REQUEST_PTY:
809 str_subtype = "pty";
810 break;
811 case SSH_CHANNEL_REQUEST_EXEC:
812 str_subtype = "exec";
813 break;
814 case SSH_CHANNEL_REQUEST_SHELL:
815 str_subtype = "shell";
816 break;
817 case SSH_CHANNEL_REQUEST_ENV:
818 str_subtype = "env";
819 break;
820 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
821 str_subtype = "subsystem";
822 break;
823 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
824 str_subtype = "window-change";
825 break;
826 case SSH_CHANNEL_REQUEST_X11:
827 str_subtype = "x11";
828 break;
829 case SSH_CHANNEL_REQUEST_UNKNOWN:
830 /* fallthrough */
831 default:
832 str_subtype = "unknown";
833 break;
834 }
835 break;
836
837 case SSH_REQUEST_SERVICE:
838 str_type = "request-service";
839 str_subtype = ssh_message_service_service(msg);
840 break;
841
842 case SSH_REQUEST_GLOBAL:
843 str_type = "request-global";
844 switch (subtype) {
845 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
846 str_subtype = "tcpip-forward";
847 break;
848 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
849 str_subtype = "cancel-tcpip-forward";
850 break;
851 case SSH_GLOBAL_REQUEST_UNKNOWN:
852 /* fallthrough */
853 default:
854 str_subtype = "unknown";
855 break;
856 }
857 break;
858
859 default:
860 str_type = "unknown";
861 str_subtype = "unknown";
862 break;
863 }
864
865 VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vaskoce319162016-02-03 15:33:08 +0100866 if ((session->status == NC_STATUS_CLOSING) || (session->status == NC_STATUS_INVALID)) {
867 /* "valid" situation if, for example, receiving some auth or channel request timeouted,
868 * but we got it now, during session free */
869 VRB("SSH message arrived on a %s session, the request will be denied.",
870 (session->status == NC_STATUS_CLOSING ? "closing" : "invalid"));
871 ssh_message_reply_default(msg);
872 return 0;
873 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100874 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +0100875
876 /*
877 * process known messages
878 */
879 if (type == SSH_REQUEST_AUTH) {
880 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
881 ERR("User \"%s\" authenticated, but requested another authentication.", session->username);
882 ssh_message_reply_default(msg);
883 return 0;
884 }
885
Michal Vasko2e6defd2016-10-07 15:48:15 +0200886 if (session->opts.server.ssh_auth_attempts >= ((struct nc_server_ssh_opts *)session->data)->auth_attempts) {
Michal Vasko086311b2016-01-08 09:53:11 +0100887 /* too many failed attempts */
888 ssh_message_reply_default(msg);
889 return 0;
890 }
891
892 /* save the username, do not let the client change it */
893 username = ssh_message_auth_user(msg);
894 if (!session->username) {
895 if (!username) {
896 ERR("Denying an auth request without a username.");
897 return 1;
898 }
899
Michal Vasko05ba9df2016-01-13 14:40:27 +0100900 session->username = lydict_insert(server_opts.ctx, username, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100901 } else if (username) {
902 if (strcmp(username, session->username)) {
903 ERR("User \"%s\" changed its username to \"%s\".", session->username, username);
904 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +0100905 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +0100906 return 1;
907 }
908 }
909
910 if (subtype == SSH_AUTH_METHOD_NONE) {
911 /* libssh will return the supported auth methods */
912 return 1;
913 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
914 nc_sshcb_auth_password(session, msg);
915 return 0;
916 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
917 nc_sshcb_auth_pubkey(session, msg);
918 return 0;
919 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
920 nc_sshcb_auth_kbdint(session, msg);
921 return 0;
922 }
923 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +0100924 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100925 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100926 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100927 }
Michal Vasko086311b2016-01-08 09:53:11 +0100928 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +0100929
Michal Vasko0df67562016-01-21 15:50:11 +0100930 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100931 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
932 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +0100933 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +0100934 } else {
935 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100936 }
937 return 0;
938 }
939 }
940
941 /* we did not process it */
942 return 1;
943}
944
Michal Vasko1a38c862016-01-15 15:50:07 +0100945/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +0100946static int
947nc_open_netconf_channel(struct nc_session *session, int timeout)
948{
Michal Vasko62be1ce2016-03-03 13:24:52 +0100949 int elapsed_usec = 0, ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100950
951 /* message callback is executed twice to give chance for the channel to be
952 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100953 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +0100954 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100955 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +0100956 return -1;
957 }
958
Michal vasko50cc94f2016-10-04 13:46:20 +0200959 ret = nc_timedlock(session->ti_lock, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100960 if (ret != 1) {
961 return ret;
962 }
963
964 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
965 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100966 ERR("Failed to receive SSH messages on a session (%s).",
967 ssh_get_error(session->ti.libssh.session));
Michal Vasko11d142a2016-01-19 15:58:24 +0100968 pthread_mutex_unlock(session->ti_lock);
Michal Vasko086311b2016-01-08 09:53:11 +0100969 return -1;
970 }
971
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100972 if (!session->ti.libssh.channel) {
973 /* we did not receive channel-open, timeout */
974 pthread_mutex_unlock(session->ti_lock);
975 return 0;
976 }
977
978 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
979 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100980 ERR("Failed to receive SSH messages on a session (%s).",
981 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100982 pthread_mutex_unlock(session->ti_lock);
983 return -1;
984 }
985 pthread_mutex_unlock(session->ti_lock);
986
987 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
988 /* we did not receive subsystem-request, timeout */
989 return 0;
990 }
991
992 return 1;
993 }
994
995 while (1) {
996 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100997 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100998 return -1;
999 }
1000
Michal vasko50cc94f2016-10-04 13:46:20 +02001001 ret = nc_timedlock(session->ti_lock, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001002 if (ret != 1) {
1003 return ret;
1004 }
1005
1006 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1007 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001008 ERR("Failed to receive SSH messages on a session (%s).",
1009 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001010 pthread_mutex_unlock(session->ti_lock);
1011 return -1;
1012 }
1013
1014 pthread_mutex_unlock(session->ti_lock);
1015
Michal Vasko086311b2016-01-08 09:53:11 +01001016 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001017 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001018 }
1019
Michal Vasko105bf272016-02-03 15:34:35 +01001020 if ((timeout != -1) && (elapsed_usec / 1000 >= timeout)) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001021 /* timeout */
Michal Vasko842522c2016-03-01 09:20:13 +01001022 ERR("Failed to start \"netconf\" SSH subsystem for too long, disconnecting.");
Michal Vasko086311b2016-01-08 09:53:11 +01001023 break;
1024 }
1025
Michal Vasko086311b2016-01-08 09:53:11 +01001026 usleep(NC_TIMEOUT_STEP);
Michal Vasko105bf272016-02-03 15:34:35 +01001027 elapsed_usec += NC_TIMEOUT_STEP;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001028 }
Michal Vasko086311b2016-01-08 09:53:11 +01001029
Michal Vasko1a38c862016-01-15 15:50:07 +01001030 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001031}
1032
Michal Vasko96164bf2016-01-21 15:41:58 +01001033int
Michal Vasko0190bc32016-03-02 15:47:49 +01001034nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko3031aae2016-01-27 16:07:18 +01001035{
Michal Vaskoe2713da2016-08-22 16:06:40 +02001036 ssh_bind sbind;
Michal Vasko3031aae2016-01-27 16:07:18 +01001037 struct nc_server_ssh_opts *opts;
Michal Vasko72387da2016-02-02 15:52:41 +01001038 int libssh_auth_methods = 0, elapsed_usec = 0, ret;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001039 uint8_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001040
Michal Vasko2cc4c682016-03-01 09:16:48 +01001041 opts = session->data;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001042
Michal Vasko086311b2016-01-08 09:53:11 +01001043 /* other transport-specific data */
1044 session->ti_type = NC_TI_LIBSSH;
1045 session->ti.libssh.session = ssh_new();
1046 if (!session->ti.libssh.session) {
Michal Vaskod083db62016-01-19 10:31:29 +01001047 ERR("Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001048 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001049 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001050 }
1051
Michal Vaskoc61c4492016-01-25 11:13:34 +01001052 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001053 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1054 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001055 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001056 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1057 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001058 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001059 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1060 }
1061 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1062
Michal Vaskoe2713da2016-08-22 16:06:40 +02001063 sbind = ssh_bind_new();
1064 if (!sbind) {
1065 ERR("Failed to create an SSH bind.");
1066 close(sock);
1067 return -1;
1068 }
1069 for (i = 0; i < opts->hostkey_count; ++i) {
1070 if (ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_HOSTKEY, opts->hostkeys[i]) != SSH_OK) {
1071 ERR("Failed to set hostkey \"%s\" (%s).", opts->hostkeys[i], ssh_get_error(sbind));
1072 close(sock);
1073 ssh_bind_free(sbind);
1074 return -1;
1075 }
1076 }
1077 if (opts->banner) {
1078 ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_BANNER, opts->banner);
1079 }
1080
Michal Vasko086311b2016-01-08 09:53:11 +01001081 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001082 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001083 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001084
Michal Vaskoe2713da2016-08-22 16:06:40 +02001085 if (ssh_bind_accept_fd(sbind, session->ti.libssh.session, sock) == SSH_ERROR) {
1086 ERR("SSH failed to accept a new connection (%s).", ssh_get_error(sbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001087 close(sock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001088 ssh_bind_free(sbind);
Michal Vasko9e036d52016-01-08 10:49:26 +01001089 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001090 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001091 ssh_bind_free(sbind);
Michal Vasko086311b2016-01-08 09:53:11 +01001092
Michal Vasko0190bc32016-03-02 15:47:49 +01001093 ssh_set_blocking(session->ti.libssh.session, 0);
1094
1095 while ((ret = ssh_handle_key_exchange(session->ti.libssh.session)) == SSH_AGAIN) {
Michal Vaskoe65b4492016-03-03 11:57:51 +01001096 /* this tends to take longer */
1097 usleep(NC_TIMEOUT_STEP * 20);
1098 elapsed_usec += NC_TIMEOUT_STEP * 20;
Michal Vasko0190bc32016-03-02 15:47:49 +01001099 if ((timeout > -1) && (elapsed_usec / 1000 >= timeout)) {
1100 break;
1101 }
1102 }
1103 if (ret == SSH_AGAIN) {
1104 ERR("SSH key exchange timeout.");
1105 return 0;
1106 } else if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001107 ERR("SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001108 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001109 }
1110
1111 /* authenticate */
Michal Vasko0190bc32016-03-02 15:47:49 +01001112 elapsed_usec = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001113 do {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001114 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001115 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001116 return -1;
1117 }
1118
Michal Vasko086311b2016-01-08 09:53:11 +01001119 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001120 ERR("Failed to receive SSH messages on a session (%s).",
1121 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001122 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001123 }
1124
1125 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1126 break;
1127 }
1128
1129 usleep(NC_TIMEOUT_STEP);
Michal Vasko72387da2016-02-02 15:52:41 +01001130 elapsed_usec += NC_TIMEOUT_STEP;
Michal Vaskoab639942016-02-29 16:23:47 +01001131 } while (!opts->auth_timeout || (elapsed_usec / 1000000 < opts->auth_timeout));
Michal Vasko086311b2016-01-08 09:53:11 +01001132
1133 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1134 /* timeout */
Michal Vasko842522c2016-03-01 09:20:13 +01001135 ERR("Client failed to authenticate for too long, disconnecting.");
Michal Vasko1a38c862016-01-15 15:50:07 +01001136 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001137 }
1138
Michal Vasko086311b2016-01-08 09:53:11 +01001139 /* open channel */
Michal Vaskoab639942016-02-29 16:23:47 +01001140 ret = nc_open_netconf_channel(session, opts->auth_timeout ? (opts->auth_timeout * 1000 - elapsed_usec / 1000) : -1);
Michal Vasko1a38c862016-01-15 15:50:07 +01001141 if (ret < 1) {
1142 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001143 }
1144
Michal Vasko96164bf2016-01-21 15:41:58 +01001145 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
Michal Vasko1a38c862016-01-15 15:50:07 +01001146 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001147}
1148
Michal Vasko71090fc2016-05-24 16:37:28 +02001149API NC_MSG_TYPE
1150nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session **session)
1151{
1152 NC_MSG_TYPE msgtype;
1153 struct nc_session *new_session = NULL;
1154
1155 if (!orig_session) {
1156 ERRARG("orig_session");
1157 return NC_MSG_ERROR;
1158 } else if (!session) {
1159 ERRARG("session");
1160 return NC_MSG_ERROR;
1161 }
1162
1163 if ((orig_session->status == NC_STATUS_RUNNING) && (orig_session->ti_type == NC_TI_LIBSSH)
1164 && orig_session->ti.libssh.next) {
1165 for (new_session = orig_session->ti.libssh.next;
1166 new_session != orig_session;
1167 new_session = new_session->ti.libssh.next) {
1168 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1169 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1170 /* we found our session */
1171 break;
1172 }
1173 }
1174 if (new_session == orig_session) {
1175 new_session = NULL;
1176 }
1177 }
1178
1179 if (!new_session) {
1180 ERR("Session does not have a NETCONF SSH channel ready.");
1181 return NC_MSG_ERROR;
1182 }
1183
1184 /* assign new SID atomically */
1185 pthread_spin_lock(&server_opts.sid_lock);
1186 new_session->id = server_opts.new_session_id++;
1187 pthread_spin_unlock(&server_opts.sid_lock);
1188
1189 /* NETCONF handshake */
1190 msgtype = nc_handshake(new_session);
1191 if (msgtype != NC_MSG_HELLO) {
1192 return msgtype;
1193 }
1194
Michal Vasko2e6defd2016-10-07 15:48:15 +02001195 new_session->opts.server.session_start = new_session->opts.server.last_rpc = time(NULL);
Michal Vasko71090fc2016-05-24 16:37:28 +02001196 new_session->status = NC_STATUS_RUNNING;
1197 *session = new_session;
1198
1199 return msgtype;
1200}
1201
1202API NC_MSG_TYPE
Michal Vasko96164bf2016-01-21 15:41:58 +01001203nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001204{
Michal Vaskobdcf2362016-07-26 11:35:43 +02001205 uint8_t q_id;
Michal Vasko71090fc2016-05-24 16:37:28 +02001206 NC_MSG_TYPE msgtype;
Michal Vasko96164bf2016-01-21 15:41:58 +01001207 struct nc_session *new_session = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001208 uint16_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001209
Michal Vasko45e53ae2016-04-07 11:46:03 +02001210 if (!ps) {
1211 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001212 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001213 } else if (!session) {
1214 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001215 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01001216 }
1217
Michal Vasko48a63ed2016-03-01 09:48:21 +01001218 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001219 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001220 return NC_MSG_ERROR;
Michal Vaskof04a52a2016-04-07 10:52:10 +02001221 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001222
Michal Vasko96164bf2016-01-21 15:41:58 +01001223 for (i = 0; i < ps->session_count; ++i) {
1224 if ((ps->sessions[i]->status == NC_STATUS_RUNNING) && (ps->sessions[i]->ti_type == NC_TI_LIBSSH)
1225 && ps->sessions[i]->ti.libssh.next) {
1226 /* an SSH session with more channels */
1227 for (new_session = ps->sessions[i]->ti.libssh.next;
1228 new_session != ps->sessions[i];
1229 new_session = new_session->ti.libssh.next) {
1230 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1231 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1232 /* we found our session */
1233 break;
1234 }
1235 }
1236 if (new_session != ps->sessions[i]) {
1237 break;
1238 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001239
Michal Vasko96164bf2016-01-21 15:41:58 +01001240 new_session = NULL;
1241 }
1242 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001243
Michal Vasko48a63ed2016-03-01 09:48:21 +01001244 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001245 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001246
Michal Vasko96164bf2016-01-21 15:41:58 +01001247 if (!new_session) {
1248 ERR("No session with a NETCONF SSH channel ready was found.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001249 return NC_MSG_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001250 }
1251
1252 /* assign new SID atomically */
1253 pthread_spin_lock(&server_opts.sid_lock);
1254 new_session->id = server_opts.new_session_id++;
1255 pthread_spin_unlock(&server_opts.sid_lock);
Michal Vaskofb89d772016-01-08 12:25:35 +01001256
Michal Vasko086311b2016-01-08 09:53:11 +01001257 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001258 msgtype = nc_handshake(new_session);
1259 if (msgtype != NC_MSG_HELLO) {
1260 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001261 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001262
Michal Vasko2e6defd2016-10-07 15:48:15 +02001263 new_session->opts.server.session_start = new_session->opts.server.last_rpc = time(NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001264 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001265 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001266
Michal Vasko71090fc2016-05-24 16:37:28 +02001267 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001268}