blob: fadc3cd5d796693cf41d679f64f0997a8af59b58 [file] [log] [blame]
Michal Vasko086311b2016-01-08 09:53:11 +01001/**
2 * \file session_server_ssh.c
3 * \author Michal Vasko <mvasko@cesnet.cz>
4 * \brief libnetconf2 SSH server session manipulation functions
5 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of the Company nor the names of its contributors
18 * may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 *
21 */
22
23#define _GNU_SOURCE
24
25#include <stdlib.h>
26#include <string.h>
27#include <sys/types.h>
28#include <pwd.h>
29#include <shadow.h>
30#include <crypt.h>
31#include <errno.h>
32
Michal Vasko11d142a2016-01-19 15:58:24 +010033#include "session_server.h"
Michal Vaskoe22c6732016-01-29 11:03:02 +010034#include "session_server_ch.h"
35#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010036
Michal Vasko3031aae2016-01-27 16:07:18 +010037struct nc_server_ssh_opts ssh_ch_opts = {
38 .auth_methods = NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE,
39 .auth_attempts = 3,
40 .auth_timeout = 10
41};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010042pthread_mutex_t ssh_ch_opts_lock = PTHREAD_MUTEX_INITIALIZER;
Michal Vasko086311b2016-01-08 09:53:11 +010043extern struct nc_server_opts server_opts;
Michal Vaskob05053d2016-01-22 16:12:06 +010044
Michal Vasko3031aae2016-01-27 16:07:18 +010045API int
46nc_server_ssh_add_endpt_listen(const char *name, const char *address, uint16_t port)
47{
48 return nc_server_add_endpt_listen(name, address, port, NC_TI_LIBSSH);
49}
Michal Vasko086311b2016-01-08 09:53:11 +010050
Michal Vasko3031aae2016-01-27 16:07:18 +010051API int
Michal Vaskoda514772016-02-01 11:32:01 +010052nc_server_ssh_endpt_set_address(const char *endpt_name, const char *address)
53{
54 return nc_server_endpt_set_address_port(endpt_name, address, 0, NC_TI_LIBSSH);
55}
56
57API int
58nc_server_ssh_endpt_set_port(const char *endpt_name, uint16_t port)
59{
60 return nc_server_endpt_set_address_port(endpt_name, NULL, port, NC_TI_LIBSSH);
61}
62
63API int
Michal Vasko3031aae2016-01-27 16:07:18 +010064nc_server_ssh_del_endpt(const char *name)
65{
66 return nc_server_del_endpt(name, NC_TI_LIBSSH);
67}
Michal Vaskob05053d2016-01-22 16:12:06 +010068
69static int
Michal Vasko3031aae2016-01-27 16:07:18 +010070nc_server_ssh_set_hostkey(const char *privkey_path, struct nc_server_ssh_opts *opts)
Michal Vasko086311b2016-01-08 09:53:11 +010071{
Michal Vasko1a38c862016-01-15 15:50:07 +010072 if (!privkey_path) {
Michal Vasko086311b2016-01-08 09:53:11 +010073 ERRARG;
74 return -1;
75 }
76
Michal Vaskob05053d2016-01-22 16:12:06 +010077 if (!opts->sshbind) {
78 opts->sshbind = ssh_bind_new();
79 if (!opts->sshbind) {
Michal Vaskod083db62016-01-19 10:31:29 +010080 ERR("Failed to create a new ssh_bind.");
Michal Vasko5fcc7142016-02-02 12:21:10 +010081 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +010082 }
83 }
84
Michal Vaskob05053d2016-01-22 16:12:06 +010085 if (ssh_bind_options_set(opts->sshbind, SSH_BIND_OPTIONS_HOSTKEY, privkey_path) != SSH_OK) {
Michal Vaskoc61c4492016-01-25 11:13:34 +010086 if (eaccess(privkey_path, R_OK)) {
87 ERR("Failed to set host key (%s).", strerror(errno));
88 } else {
89 ERR("Failed to set host key (%s).", ssh_get_error(opts->sshbind));
90 }
Michal Vasko5fcc7142016-02-02 12:21:10 +010091 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +010092 }
Michal Vaskod45e25a2016-01-08 15:48:44 +010093
Michal Vasko5fcc7142016-02-02 12:21:10 +010094 return 0;
Michal Vaskob05053d2016-01-22 16:12:06 +010095}
96
97API int
Michal Vasko3031aae2016-01-27 16:07:18 +010098nc_server_ssh_endpt_set_hostkey(const char *endpt_name, const char *privkey_path)
Michal Vaskob05053d2016-01-22 16:12:06 +010099{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100100 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100101 struct nc_endpt *endpt;
102
Michal Vasko51e514d2016-02-02 15:51:52 +0100103 /* LOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100104 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100105 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100106 return -1;
107 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100108 ret = nc_server_ssh_set_hostkey(privkey_path, endpt->ti_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100109 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100110 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100111
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100112 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100113}
114
115API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100116nc_server_ssh_ch_set_hostkey(const char *privkey_path)
Michal Vaskob05053d2016-01-22 16:12:06 +0100117{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100118 int ret;
119
120 /* OPTS LOCK */
121 pthread_mutex_lock(&ssh_ch_opts_lock);
122 ret = nc_server_ssh_set_hostkey(privkey_path, &ssh_ch_opts);
123 /* OPTS UNLOCK */
124 pthread_mutex_unlock(&ssh_ch_opts_lock);
125
126 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100127}
128
129static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100130nc_server_ssh_set_banner(const char *banner, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100131{
Michal Vaskob05053d2016-01-22 16:12:06 +0100132 if (!banner) {
133 ERRARG;
134 return -1;
135 }
136
Michal Vaskob05053d2016-01-22 16:12:06 +0100137 if (!opts->sshbind) {
138 opts->sshbind = ssh_bind_new();
139 if (!opts->sshbind) {
140 ERR("Failed to create a new ssh_bind.");
Michal Vasko5fcc7142016-02-02 12:21:10 +0100141 return -1;
Michal Vaskob05053d2016-01-22 16:12:06 +0100142 }
143 }
144
145 ssh_bind_options_set(opts->sshbind, SSH_BIND_OPTIONS_BANNER, banner);
146
Michal Vaskob05053d2016-01-22 16:12:06 +0100147 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100148}
149
150API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100151nc_server_ssh_endpt_set_banner(const char *endpt_name, const char *banner)
Michal Vasko086311b2016-01-08 09:53:11 +0100152{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100153 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100154 struct nc_endpt *endpt;
155
Michal Vasko51e514d2016-02-02 15:51:52 +0100156 /* LOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100157 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100158 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100159 return -1;
160 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100161 ret = nc_server_ssh_set_banner(banner, endpt->ti_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100162 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100163 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100164
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100165 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100166}
167
168API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100169nc_server_ssh_ch_set_banner(const char *banner)
Michal Vasko086311b2016-01-08 09:53:11 +0100170{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100171 int ret;
172
173 /* OPTS LOCK */
174 pthread_mutex_lock(&ssh_ch_opts_lock);
175 ret = nc_server_ssh_set_banner(banner, &ssh_ch_opts);
176 /* OPTS UNLOCK */
177 pthread_mutex_unlock(&ssh_ch_opts_lock);
178
179 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100180}
181
182static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100183nc_server_ssh_set_auth_methods(int auth_methods, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100184{
Michal Vasko086311b2016-01-08 09:53:11 +0100185 if (!(auth_methods & NC_SSH_AUTH_PUBLICKEY) && !(auth_methods & NC_SSH_AUTH_PASSWORD)
186 && !(auth_methods & NC_SSH_AUTH_INTERACTIVE)) {
187 ERRARG;
188 return -1;
189 }
190
Michal Vaskob05053d2016-01-22 16:12:06 +0100191 opts->auth_methods = auth_methods;
192 return 0;
193}
194
195API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100196nc_server_ssh_endpt_set_auth_methods(const char *endpt_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100197{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100198 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100199 struct nc_endpt *endpt;
200
Michal Vasko51e514d2016-02-02 15:51:52 +0100201 /* LOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100202 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100203 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100204 return -1;
205 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100206 ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->ti_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100207 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100208 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100209
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100210 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100211}
212
213API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100214nc_server_ssh_ch_set_auth_methods(int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100215{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100216 int ret;
217
218 /* OPTS LOCK */
219 pthread_mutex_lock(&ssh_ch_opts_lock);
220 ret = nc_server_ssh_set_auth_methods(auth_methods, &ssh_ch_opts);
221 /* OPTS UNLOCK */
222 pthread_mutex_unlock(&ssh_ch_opts_lock);
223
224 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100225}
226
227static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100228nc_server_ssh_set_auth_attempts(uint16_t auth_attempts, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100229{
Michal Vaskob05053d2016-01-22 16:12:06 +0100230 if (!auth_attempts) {
231 ERRARG;
232 return -1;
233 }
234
Michal Vaskob05053d2016-01-22 16:12:06 +0100235 opts->auth_attempts = auth_attempts;
Michal Vasko086311b2016-01-08 09:53:11 +0100236 return 0;
237}
238
239API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100240nc_server_ssh_endpt_set_auth_attempts(const char *endpt_name, uint16_t auth_attempts)
Michal Vasko086311b2016-01-08 09:53:11 +0100241{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100242 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100243 struct nc_endpt *endpt;
244
Michal Vasko51e514d2016-02-02 15:51:52 +0100245 /* LOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100246 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100247 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100248 return -1;
249 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100250 ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->ti_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100251 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100252 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100253
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100254 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100255}
256
257API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100258nc_server_ssh_set_ch_auth_attempts(uint16_t auth_attempts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100259{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100260 int ret;
261
262 /* OPTS LOCK */
263 pthread_mutex_lock(&ssh_ch_opts_lock);
264 ret = nc_server_ssh_set_auth_attempts(auth_attempts, &ssh_ch_opts);
265 /* OPTS UNLOCK */
266 pthread_mutex_unlock(&ssh_ch_opts_lock);
267
268 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100269}
270
271static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100272nc_server_ssh_set_auth_timeout(uint16_t auth_timeout, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100273{
Michal Vaskob05053d2016-01-22 16:12:06 +0100274 if (!auth_timeout) {
Michal Vasko086311b2016-01-08 09:53:11 +0100275 ERRARG;
276 return -1;
277 }
278
Michal Vaskob05053d2016-01-22 16:12:06 +0100279 opts->auth_timeout = auth_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100280 return 0;
281}
282
283API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100284nc_server_ssh_endpt_set_auth_timeout(const char *endpt_name, uint16_t auth_timeout)
Michal Vasko086311b2016-01-08 09:53:11 +0100285{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100286 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100287 struct nc_endpt *endpt;
288
Michal Vasko51e514d2016-02-02 15:51:52 +0100289 /* LOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100290 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100291 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100292 return -1;
293 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100294 ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->ti_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100295 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100296 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100297
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100298 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100299}
300
301API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100302nc_server_ssh_ch_set_auth_timeout(uint16_t auth_timeout)
Michal Vaskob05053d2016-01-22 16:12:06 +0100303{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100304 int ret;
305
306 /* OPTS LOCK */
307 pthread_mutex_lock(&ssh_ch_opts_lock);
308 ret = nc_server_ssh_set_auth_timeout(auth_timeout, &ssh_ch_opts);
309 /* OPTS UNLOCK */
310 pthread_mutex_unlock(&ssh_ch_opts_lock);
311
312 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100313}
314
315static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100316nc_server_ssh_add_authkey(const char *pubkey_path, const char *username, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100317{
Michal Vaskob05053d2016-01-22 16:12:06 +0100318 if (!pubkey_path || !username) {
Michal Vasko086311b2016-01-08 09:53:11 +0100319 ERRARG;
320 return -1;
321 }
322
Michal Vaskob05053d2016-01-22 16:12:06 +0100323 ++opts->authkey_count;
324 opts->authkeys = realloc(opts->authkeys, opts->authkey_count * sizeof *opts->authkeys);
325
326 nc_ctx_lock(-1, NULL);
327 opts->authkeys[opts->authkey_count - 1].path = lydict_insert(server_opts.ctx, pubkey_path, 0);
328 opts->authkeys[opts->authkey_count - 1].username = lydict_insert(server_opts.ctx, username, 0);
329 nc_ctx_unlock();
330
Michal Vasko086311b2016-01-08 09:53:11 +0100331 return 0;
332}
333
334API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100335nc_server_ssh_endpt_add_authkey(const char *endpt_name, const char *pubkey_path, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100336{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100337 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100338 struct nc_endpt *endpt;
339
Michal Vasko51e514d2016-02-02 15:51:52 +0100340 /* LOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100341 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100342 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100343 return -1;
344 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100345 ret = nc_server_ssh_add_authkey(pubkey_path, username, endpt->ti_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100346 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100347 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100348
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100349 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100350}
351
352API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100353nc_server_ssh_ch_add_authkey(const char *pubkey_path, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100354{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100355 int ret;
356
357 /* OPTS LOCK */
358 pthread_mutex_lock(&ssh_ch_opts_lock);
359 ret = nc_server_ssh_add_authkey(pubkey_path, username, &ssh_ch_opts);
360 /* OPTS UNLOCK */
361 pthread_mutex_unlock(&ssh_ch_opts_lock);
362
363 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100364}
365
366static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100367nc_server_ssh_del_authkey(const char *pubkey_path, const char *username, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100368{
Michal Vasko086311b2016-01-08 09:53:11 +0100369 uint32_t i;
370 int ret = -1;
371
Michal Vasko1a38c862016-01-15 15:50:07 +0100372 if (!pubkey_path && !username) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100373 nc_ctx_lock(-1, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +0100374 for (i = 0; i < opts->authkey_count; ++i) {
375 lydict_remove(server_opts.ctx, opts->authkeys[i].path);
376 lydict_remove(server_opts.ctx, opts->authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100377
Michal Vasko086311b2016-01-08 09:53:11 +0100378 ret = 0;
379 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100380 nc_ctx_unlock();
Michal Vaskob05053d2016-01-22 16:12:06 +0100381 free(opts->authkeys);
382 opts->authkeys = NULL;
383 opts->authkey_count = 0;
Michal Vasko1a38c862016-01-15 15:50:07 +0100384 } else {
Michal Vaskob05053d2016-01-22 16:12:06 +0100385 for (i = 0; i < opts->authkey_count; ++i) {
386 if ((!pubkey_path || !strcmp(opts->authkeys[i].path, pubkey_path))
387 && (!username || !strcmp(opts->authkeys[i].username, username))) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100388 nc_ctx_lock(-1, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +0100389 lydict_remove(server_opts.ctx, opts->authkeys[i].path);
390 lydict_remove(server_opts.ctx, opts->authkeys[i].username);
Michal Vasko11d142a2016-01-19 15:58:24 +0100391 nc_ctx_unlock();
Michal Vasko1a38c862016-01-15 15:50:07 +0100392
Michal Vaskob05053d2016-01-22 16:12:06 +0100393 --opts->authkey_count;
Michal Vaskoc0256492016-02-02 12:19:06 +0100394 if (i < opts->authkey_count) {
395 memcpy(&opts->authkeys[i], &opts->authkeys[opts->authkey_count], sizeof *opts->authkeys);
396 } else if (!opts->authkey_count) {
397 free(opts->authkeys);
398 opts->authkeys = NULL;
399 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100400
401 ret = 0;
402 }
403 }
Michal Vasko086311b2016-01-08 09:53:11 +0100404 }
405
406 return ret;
407}
408
Michal Vaskob05053d2016-01-22 16:12:06 +0100409API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100410nc_server_ssh_endpt_del_authkey(const char *endpt_name, const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100411{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100412 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100413 struct nc_endpt *endpt;
414
Michal Vasko51e514d2016-02-02 15:51:52 +0100415 /* LOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100416 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100417 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100418 return -1;
419 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100420 ret = nc_server_ssh_del_authkey(pubkey_path, username, endpt->ti_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100421 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100422 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100423
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100424 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100425}
426
427API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100428nc_server_ssh_ch_del_authkey(const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100429{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100430 int ret;
431
432 /* OPTS LOCK */
433 pthread_mutex_lock(&ssh_ch_opts_lock);
434 ret = nc_server_ssh_del_authkey(pubkey_path, username, &ssh_ch_opts);
435 /* OPTS UNLOCK */
436 pthread_mutex_unlock(&ssh_ch_opts_lock);
437
438 return ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100439}
440
441void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100442nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100443{
444 if (opts->sshbind) {
445 ssh_bind_free(opts->sshbind);
446 opts->sshbind = NULL;
447 }
448
449 nc_server_ssh_del_authkey(NULL, NULL, opts);
Michal Vaskob05053d2016-01-22 16:12:06 +0100450}
451
Michal Vasko086311b2016-01-08 09:53:11 +0100452API void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100453nc_server_ssh_ch_clear_opts(void)
Michal Vasko086311b2016-01-08 09:53:11 +0100454{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100455 /* OPTS LOCK */
456 pthread_mutex_lock(&ssh_ch_opts_lock);
457 nc_server_ssh_clear_opts(&ssh_ch_opts);
458 /* OPTS UNLOCK */
459 pthread_mutex_unlock(&ssh_ch_opts_lock);
Michal Vasko086311b2016-01-08 09:53:11 +0100460}
461
462static char *
463auth_password_get_pwd_hash(const char *username)
464{
465 struct passwd *pwd, pwd_buf;
466 struct spwd *spwd, spwd_buf;
467 char *pass_hash = NULL, buf[256];
468
469 getpwnam_r(username, &pwd_buf, buf, 256, &pwd);
470 if (!pwd) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100471 VRB("User \"%s\" not found locally.", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100472 return NULL;
473 }
474
475 if (!strcmp(pwd->pw_passwd, "x")) {
476 getspnam_r(username, &spwd_buf, buf, 256, &spwd);
477 if (!spwd) {
478 VRB("Failed to retrieve the shadow entry for \"%s\".", username);
479 return NULL;
480 }
481
482 pass_hash = spwd->sp_pwdp;
483 } else {
484 pass_hash = pwd->pw_passwd;
485 }
486
487 if (!pass_hash) {
Michal Vaskod083db62016-01-19 10:31:29 +0100488 ERR("No password could be retrieved for \"%s\".", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100489 return NULL;
490 }
491
492 /* check the hash structure for special meaning */
493 if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) {
494 VRB("User \"%s\" is not allowed to authenticate using a password.", username);
495 return NULL;
496 }
497 if (!strcmp(pass_hash, "*NP*")) {
498 VRB("Retrieving password for \"%s\" from a NIS+ server not supported.", username);
499 return NULL;
500 }
501
502 return strdup(pass_hash);
503}
504
505static int
506auth_password_compare_pwd(const char *pass_hash, const char *pass_clear)
507{
508 char *new_pass_hash;
509 struct crypt_data cdata;
510
511 if (!pass_hash[0]) {
512 if (!pass_clear[0]) {
513 WRN("User authentication successful with an empty password!");
514 return 0;
515 } else {
516 /* the user did now know he does not need any password,
517 * (which should not be used) so deny authentication */
518 return 1;
519 }
520 }
521
522 cdata.initialized = 0;
523 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
524 return strcmp(new_pass_hash, pass_hash);
525}
526
527static void
528nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
529{
530 char *pass_hash;
531
532 pass_hash = auth_password_get_pwd_hash(session->username);
533 if (pass_hash && !auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100534 VRB("User \"%s\" authenticated.", session->username);
Michal Vasko086311b2016-01-08 09:53:11 +0100535 ssh_message_auth_reply_success(msg, 0);
536 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
537 free(pass_hash);
538 return;
539 }
540
541 free(pass_hash);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100542 ++session->ssh_auth_attempts;
Michal Vaskod083db62016-01-19 10:31:29 +0100543 VRB("Failed user \"'%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100544 ssh_message_reply_default(msg);
545}
546
547static void
548nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
549{
550 char *pass_hash;
551
552 if (!ssh_message_auth_kbdint_is_response(msg)) {
553 const char *prompts[] = {"Password: "};
554 char echo[] = {0};
555
556 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
557 } else {
558 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {
559 ssh_message_reply_default(msg);
560 return;
561 }
562 pass_hash = auth_password_get_pwd_hash(session->username);
563 if (!pass_hash) {
564 ssh_message_reply_default(msg);
565 return;
566 }
567 if (!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0))) {
568 VRB("User \"%s\" authenticated.", session->username);
569 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
570 ssh_message_auth_reply_success(msg, 0);
571 } else {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100572 ++session->ssh_auth_attempts;
573 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100574 ssh_message_reply_default(msg);
575 }
576 }
577}
578
579static const char *
Michal Vasko3031aae2016-01-27 16:07:18 +0100580auth_pubkey_compare_key(struct nc_server_ssh_opts *opts, ssh_key key)
Michal Vasko086311b2016-01-08 09:53:11 +0100581{
582 uint32_t i;
583 ssh_key pub_key;
Michal Vasko76e3a352016-01-18 09:07:00 +0100584 const char *username = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100585
Michal Vasko3031aae2016-01-27 16:07:18 +0100586 for (i = 0; i < opts->authkey_count; ++i) {
587 if (ssh_pki_import_pubkey_file(opts->authkeys[i].path, &pub_key) != SSH_OK) {
588 if (eaccess(opts->authkeys[i].path, R_OK)) {
589 WRN("Failed to import the public key \"%s\" (%s).", opts->authkeys[i].path, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100590 } else {
Michal Vaskoe5612cf2016-02-02 15:52:16 +0100591 WRN("Failed to import the public key \"%s\" (%s).", opts->authkeys[i].path, ssh_get_error(pub_key));
Michal Vasko086311b2016-01-08 09:53:11 +0100592 }
593 continue;
594 }
595
596 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
597 ssh_key_free(pub_key);
598 break;
599 }
600
601 ssh_key_free(pub_key);
602 }
603
Michal Vasko3031aae2016-01-27 16:07:18 +0100604 if (i < opts->authkey_count) {
605 username = opts->authkeys[i].username;
Michal Vasko086311b2016-01-08 09:53:11 +0100606 }
607
608 return username;
609}
610
611static void
612nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
613{
614 const char *username;
615 int signature_state;
616
617 signature_state = ssh_message_auth_publickey_state(msg);
618 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
619 VRB("User \"%s\" authenticated.", session->username);
620 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
621 ssh_message_auth_reply_success(msg, 0);
622 return;
623
624 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100625 if ((username = auth_pubkey_compare_key(session->ti_opts, ssh_message_auth_pubkey(msg))) == NULL) {
Michal Vasko086311b2016-01-08 09:53:11 +0100626 VRB("User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
627
628 } else if (strcmp(session->username, username)) {
629 VRB("User \"%s\" is not the username identified with the presented public key.", session->username);
630
631 } else {
632 /* accepting only the use of a public key */
633 ssh_message_auth_reply_pk_ok_simple(msg);
634 return;
635 }
636 }
637
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100638 ++session->ssh_auth_attempts;
639 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100640 ssh_message_reply_default(msg);
641}
642
643static int
Michal Vasko96164bf2016-01-21 15:41:58 +0100644nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100645{
Michal Vasko96164bf2016-01-21 15:41:58 +0100646 ssh_channel chan;
647
648 /* first channel request */
649 if (!session->ti.libssh.channel) {
650 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100651 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100652 return -1;
653 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100654 chan = ssh_message_channel_request_open_reply_accept(msg);
655 if (!chan) {
656 ERR("Failed to create a new SSH channel.");
657 return -1;
658 }
659 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +0100660
Michal Vasko96164bf2016-01-21 15:41:58 +0100661 /* additional channel request */
662 } else {
663 chan = ssh_message_channel_request_open_reply_accept(msg);
664 if (!chan) {
665 ERR("Session %u: failed to create a new SSH channel.", session->id);
666 return -1;
667 }
668 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +0100669 }
670
Michal Vasko086311b2016-01-08 09:53:11 +0100671 return 0;
672}
673
674static int
675nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
676{
Michal Vasko96164bf2016-01-21 15:41:58 +0100677 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +0100678
Michal Vasko96164bf2016-01-21 15:41:58 +0100679 if (strcmp(subsystem, "netconf")) {
680 WRN("Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +0100681 return -1;
682 }
683
Michal Vasko96164bf2016-01-21 15:41:58 +0100684 if (session->ti.libssh.channel == channel) {
685 /* first channel requested */
686 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
687 ERRINT;
688 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100689 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100690 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
691 ERR("Session %u: subsystem \"netconf\" requested for the second time.", session->id);
692 return -1;
693 }
694
695 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +0100696 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +0100697 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
698 new_session = calloc(1, sizeof *new_session);
699
700 /* insert the new session */
701 if (!session->ti.libssh.next) {
702 new_session->ti.libssh.next = session;
703 } else {
704 new_session->ti.libssh.next = session->ti.libssh.next;
705 }
706 session->ti.libssh.next = new_session;
707
708 new_session->status = NC_STATUS_STARTING;
709 new_session->side = NC_SERVER;
710 new_session->ti_type = NC_TI_LIBSSH;
711 new_session->ti_lock = session->ti_lock;
712 new_session->ti.libssh.channel = channel;
713 new_session->ti.libssh.session = session->ti.libssh.session;
714 new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
715 new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
716 new_session->port = session->port;
717 new_session->ctx = server_opts.ctx;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100718 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX
719 | (session->flags & NC_SESSION_CALLHOME ? NC_SESSION_CALLHOME : 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100720 }
721
722 return 0;
723}
724
Michal Vasko96164bf2016-01-21 15:41:58 +0100725int
Michal Vaskob48aa812016-01-18 14:13:09 +0100726nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +0100727{
728 const char *str_type, *str_subtype = NULL, *username;
729 int subtype, type;
730 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +0100731
732 type = ssh_message_type(msg);
733 subtype = ssh_message_subtype(msg);
734
735 switch (type) {
736 case SSH_REQUEST_AUTH:
737 str_type = "request-auth";
738 switch (subtype) {
739 case SSH_AUTH_METHOD_NONE:
740 str_subtype = "none";
741 break;
742 case SSH_AUTH_METHOD_PASSWORD:
743 str_subtype = "password";
744 break;
745 case SSH_AUTH_METHOD_PUBLICKEY:
746 str_subtype = "publickey";
747 break;
748 case SSH_AUTH_METHOD_HOSTBASED:
749 str_subtype = "hostbased";
750 break;
751 case SSH_AUTH_METHOD_INTERACTIVE:
752 str_subtype = "interactive";
753 break;
754 case SSH_AUTH_METHOD_GSSAPI_MIC:
755 str_subtype = "gssapi-mic";
756 break;
757 }
758 break;
759
760 case SSH_REQUEST_CHANNEL_OPEN:
761 str_type = "request-channel-open";
762 switch (subtype) {
763 case SSH_CHANNEL_SESSION:
764 str_subtype = "session";
765 break;
766 case SSH_CHANNEL_DIRECT_TCPIP:
767 str_subtype = "direct-tcpip";
768 break;
769 case SSH_CHANNEL_FORWARDED_TCPIP:
770 str_subtype = "forwarded-tcpip";
771 break;
772 case (int)SSH_CHANNEL_X11:
773 str_subtype = "channel-x11";
774 break;
775 case SSH_CHANNEL_UNKNOWN:
776 /* fallthrough */
777 default:
778 str_subtype = "unknown";
779 break;
780 }
781 break;
782
783 case SSH_REQUEST_CHANNEL:
784 str_type = "request-channel";
785 switch (subtype) {
786 case SSH_CHANNEL_REQUEST_PTY:
787 str_subtype = "pty";
788 break;
789 case SSH_CHANNEL_REQUEST_EXEC:
790 str_subtype = "exec";
791 break;
792 case SSH_CHANNEL_REQUEST_SHELL:
793 str_subtype = "shell";
794 break;
795 case SSH_CHANNEL_REQUEST_ENV:
796 str_subtype = "env";
797 break;
798 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
799 str_subtype = "subsystem";
800 break;
801 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
802 str_subtype = "window-change";
803 break;
804 case SSH_CHANNEL_REQUEST_X11:
805 str_subtype = "x11";
806 break;
807 case SSH_CHANNEL_REQUEST_UNKNOWN:
808 /* fallthrough */
809 default:
810 str_subtype = "unknown";
811 break;
812 }
813 break;
814
815 case SSH_REQUEST_SERVICE:
816 str_type = "request-service";
817 str_subtype = ssh_message_service_service(msg);
818 break;
819
820 case SSH_REQUEST_GLOBAL:
821 str_type = "request-global";
822 switch (subtype) {
823 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
824 str_subtype = "tcpip-forward";
825 break;
826 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
827 str_subtype = "cancel-tcpip-forward";
828 break;
829 case SSH_GLOBAL_REQUEST_UNKNOWN:
830 /* fallthrough */
831 default:
832 str_subtype = "unknown";
833 break;
834 }
835 break;
836
837 default:
838 str_type = "unknown";
839 str_subtype = "unknown";
840 break;
841 }
842
843 VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vaskoce319162016-02-03 15:33:08 +0100844 if ((session->status == NC_STATUS_CLOSING) || (session->status == NC_STATUS_INVALID)) {
845 /* "valid" situation if, for example, receiving some auth or channel request timeouted,
846 * but we got it now, during session free */
847 VRB("SSH message arrived on a %s session, the request will be denied.",
848 (session->status == NC_STATUS_CLOSING ? "closing" : "invalid"));
849 ssh_message_reply_default(msg);
850 return 0;
851 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100852 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +0100853
854 /*
855 * process known messages
856 */
857 if (type == SSH_REQUEST_AUTH) {
858 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
859 ERR("User \"%s\" authenticated, but requested another authentication.", session->username);
860 ssh_message_reply_default(msg);
861 return 0;
862 }
863
Michal Vasko3031aae2016-01-27 16:07:18 +0100864 if (session->ssh_auth_attempts >= ((struct nc_server_ssh_opts *)session->ti_opts)->auth_attempts) {
Michal Vasko086311b2016-01-08 09:53:11 +0100865 /* too many failed attempts */
866 ssh_message_reply_default(msg);
867 return 0;
868 }
869
870 /* save the username, do not let the client change it */
871 username = ssh_message_auth_user(msg);
872 if (!session->username) {
873 if (!username) {
874 ERR("Denying an auth request without a username.");
875 return 1;
876 }
877
Michal Vasko05ba9df2016-01-13 14:40:27 +0100878 session->username = lydict_insert(server_opts.ctx, username, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100879 } else if (username) {
880 if (strcmp(username, session->username)) {
881 ERR("User \"%s\" changed its username to \"%s\".", session->username, username);
882 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +0100883 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +0100884 return 1;
885 }
886 }
887
888 if (subtype == SSH_AUTH_METHOD_NONE) {
889 /* libssh will return the supported auth methods */
890 return 1;
891 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
892 nc_sshcb_auth_password(session, msg);
893 return 0;
894 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
895 nc_sshcb_auth_pubkey(session, msg);
896 return 0;
897 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
898 nc_sshcb_auth_kbdint(session, msg);
899 return 0;
900 }
901 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +0100902 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100903 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100904 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100905 }
Michal Vasko086311b2016-01-08 09:53:11 +0100906 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +0100907
Michal Vasko0df67562016-01-21 15:50:11 +0100908 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100909 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
910 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +0100911 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +0100912 } else {
913 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100914 }
915 return 0;
916 }
917 }
918
919 /* we did not process it */
920 return 1;
921}
922
Michal Vasko1a38c862016-01-15 15:50:07 +0100923/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +0100924static int
925nc_open_netconf_channel(struct nc_session *session, int timeout)
926{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100927 int elapsed = 0, ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100928
929 /* message callback is executed twice to give chance for the channel to be
930 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100931 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +0100932 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100933 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +0100934 return -1;
935 }
936
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100937 ret = nc_timedlock(session->ti_lock, timeout, NULL);
938 if (ret != 1) {
939 return ret;
940 }
941
942 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
943 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100944 ERR("Failed to receive SSH messages on a session (%s).",
945 ssh_get_error(session->ti.libssh.session));
Michal Vasko11d142a2016-01-19 15:58:24 +0100946 pthread_mutex_unlock(session->ti_lock);
Michal Vasko086311b2016-01-08 09:53:11 +0100947 return -1;
948 }
949
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100950 if (!session->ti.libssh.channel) {
951 /* we did not receive channel-open, timeout */
952 pthread_mutex_unlock(session->ti_lock);
953 return 0;
954 }
955
956 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
957 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100958 ERR("Failed to receive SSH messages on a session (%s).",
959 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100960 pthread_mutex_unlock(session->ti_lock);
961 return -1;
962 }
963 pthread_mutex_unlock(session->ti_lock);
964
965 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
966 /* we did not receive subsystem-request, timeout */
967 return 0;
968 }
969
970 return 1;
971 }
972
973 while (1) {
974 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100975 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100976 return -1;
977 }
978
979 ret = nc_timedlock(session->ti_lock, timeout, &elapsed);
980 if (ret != 1) {
981 return ret;
982 }
983
984 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
985 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100986 ERR("Failed to receive SSH messages on a session (%s).",
987 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100988 pthread_mutex_unlock(session->ti_lock);
989 return -1;
990 }
991
992 pthread_mutex_unlock(session->ti_lock);
993
Michal Vasko086311b2016-01-08 09:53:11 +0100994 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100995 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +0100996 }
997
Michal Vasko1e5a9e82016-02-02 12:21:46 +0100998 if ((timeout != -1) && (elapsed >= timeout)) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100999 /* timeout */
Michal Vasko086311b2016-01-08 09:53:11 +01001000 break;
1001 }
1002
Michal Vasko086311b2016-01-08 09:53:11 +01001003 usleep(NC_TIMEOUT_STEP);
1004 elapsed += NC_TIMEOUT_STEP;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001005 }
Michal Vasko086311b2016-01-08 09:53:11 +01001006
Michal Vasko1a38c862016-01-15 15:50:07 +01001007 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001008}
1009
Michal Vasko96164bf2016-01-21 15:41:58 +01001010/* ret 0 - timeout, 1 channel has data, 2 some other channel has data,
1011 * 3 status change, 4 new SSH message, 5 new NETCONF SSH channel, -1 error */
1012int
1013nc_ssh_pollin(struct nc_session *session, int *timeout)
1014{
1015 int ret, elapsed = 0;
1016 struct nc_session *new;
1017
1018 ret = nc_timedlock(session->ti_lock, *timeout, &elapsed);
1019 if (*timeout > 0) {
1020 *timeout -= elapsed;
1021 }
1022
1023 if (ret != 1) {
1024 return ret;
1025 }
1026
1027 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1028 pthread_mutex_unlock(session->ti_lock);
1029
1030 if (ret != SSH_OK) {
1031 ERR("Session %u: failed to receive SSH messages (%s).", session->id,
1032 ssh_get_error(session->ti.libssh.session));
1033 session->status = NC_STATUS_INVALID;
1034 session->term_reason = NC_SESSION_TERM_OTHER;
1035 return 3;
1036 }
1037
1038 /* new SSH message */
1039 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1040 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1041 if (session->ti.libssh.next) {
1042 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1043 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1044 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1045 /* new NETCONF SSH channel */
1046 return 5;
1047 }
1048 }
1049 }
1050
1051 /* just some SSH message */
1052 return 4;
1053 }
1054
1055 /* no new SSH message, maybe NETCONF data? */
1056 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
1057 /* not this one */
1058 if (!ret) {
1059 return 2;
1060 } else if (ret == SSH_ERROR) {
1061 ERR("Session %u: SSH channel error (%s).", session->id,
1062 ssh_get_error(session->ti.libssh.session));
1063 session->status = NC_STATUS_INVALID;
1064 session->term_reason = NC_SESSION_TERM_OTHER;
1065 return 3;
1066 } else if (ret == SSH_EOF) {
1067 ERR("Session %u: communication channel unexpectedly closed (libssh).",
1068 session->id);
1069 session->status = NC_STATUS_INVALID;
1070 session->term_reason = NC_SESSION_TERM_DROPPED;
1071 return 3;
1072 }
1073
1074 return 1;
1075}
1076
Michal Vasko3031aae2016-01-27 16:07:18 +01001077API int
1078nc_connect_callhome_ssh(const char *host, uint16_t port, int timeout, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001079{
Michal Vasko3031aae2016-01-27 16:07:18 +01001080 return nc_connect_callhome(host, port, NC_TI_LIBSSH, timeout, session);
1081}
1082
1083int
1084nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
1085{
1086 struct nc_server_ssh_opts *opts;
Michal Vasko72387da2016-02-02 15:52:41 +01001087 int libssh_auth_methods = 0, elapsed_usec = 0, ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001088
Michal Vasko3031aae2016-01-27 16:07:18 +01001089 opts = session->ti_opts;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001090
Michal Vasko086311b2016-01-08 09:53:11 +01001091 /* other transport-specific data */
1092 session->ti_type = NC_TI_LIBSSH;
1093 session->ti.libssh.session = ssh_new();
1094 if (!session->ti.libssh.session) {
Michal Vaskod083db62016-01-19 10:31:29 +01001095 ERR("Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001096 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001097 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001098 }
1099
Michal Vaskoc61c4492016-01-25 11:13:34 +01001100 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001101 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1102 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001103 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001104 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1105 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001106 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001107 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1108 }
1109 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1110
1111 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001112 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001113 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001114
Michal Vaskoc61c4492016-01-25 11:13:34 +01001115 if (ssh_bind_accept_fd(opts->sshbind, session->ti.libssh.session, sock) == SSH_ERROR) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001116 ERR("SSH failed to accept a new connection (%s).", ssh_get_error(opts->sshbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001117 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001118 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001119 }
1120
1121 if (ssh_handle_key_exchange(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001122 ERR("SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001123 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001124 }
1125
1126 /* authenticate */
1127 do {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001128 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001129 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001130 return -1;
1131 }
1132
Michal Vasko086311b2016-01-08 09:53:11 +01001133 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001134 ERR("Failed to receive SSH messages on a session (%s).",
1135 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001136 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001137 }
1138
1139 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1140 break;
1141 }
1142
1143 usleep(NC_TIMEOUT_STEP);
Michal Vasko72387da2016-02-02 15:52:41 +01001144 elapsed_usec += NC_TIMEOUT_STEP;
1145 } while ((timeout == -1) || (timeout && (elapsed_usec / 1000 < timeout)));
Michal Vasko086311b2016-01-08 09:53:11 +01001146
1147 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1148 /* timeout */
Michal Vasko1a38c862016-01-15 15:50:07 +01001149 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001150 }
1151
1152 if (timeout > 0) {
Michal Vasko72387da2016-02-02 15:52:41 +01001153 timeout -= elapsed_usec / 1000;
Michal Vasko086311b2016-01-08 09:53:11 +01001154 }
1155
1156 /* open channel */
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001157 ret = nc_open_netconf_channel(session, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001158 if (ret < 1) {
1159 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001160 }
1161
Michal Vasko96164bf2016-01-21 15:41:58 +01001162 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1163
Michal Vasko1a38c862016-01-15 15:50:07 +01001164 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001165}
1166
Michal Vasko96164bf2016-01-21 15:41:58 +01001167API int
1168nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001169{
Michal Vasko96164bf2016-01-21 15:41:58 +01001170 struct nc_session *new_session = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001171 uint16_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001172
Michal Vasko96164bf2016-01-21 15:41:58 +01001173 if (!ps || !session) {
1174 ERRARG;
1175 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001176 }
1177
Michal Vasko96164bf2016-01-21 15:41:58 +01001178 for (i = 0; i < ps->session_count; ++i) {
1179 if ((ps->sessions[i]->status == NC_STATUS_RUNNING) && (ps->sessions[i]->ti_type == NC_TI_LIBSSH)
1180 && ps->sessions[i]->ti.libssh.next) {
1181 /* an SSH session with more channels */
1182 for (new_session = ps->sessions[i]->ti.libssh.next;
1183 new_session != ps->sessions[i];
1184 new_session = new_session->ti.libssh.next) {
1185 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1186 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1187 /* we found our session */
1188 break;
1189 }
1190 }
1191 if (new_session != ps->sessions[i]) {
1192 break;
1193 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001194
Michal Vasko96164bf2016-01-21 15:41:58 +01001195 new_session = NULL;
1196 }
1197 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001198
Michal Vasko96164bf2016-01-21 15:41:58 +01001199 if (!new_session) {
1200 ERR("No session with a NETCONF SSH channel ready was found.");
1201 return -1;
1202 }
1203
1204 /* assign new SID atomically */
1205 pthread_spin_lock(&server_opts.sid_lock);
1206 new_session->id = server_opts.new_session_id++;
1207 pthread_spin_unlock(&server_opts.sid_lock);
Michal Vaskofb89d772016-01-08 12:25:35 +01001208
Michal Vasko086311b2016-01-08 09:53:11 +01001209 /* NETCONF handshake */
1210 if (nc_handshake(new_session)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001211 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001212 }
1213 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001214 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001215
Michal Vasko96164bf2016-01-21 15:41:58 +01001216 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001217}