blob: bbcb0e3b465c0e2b84ca6e0446c467b29279c0cc [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);
Michal Vaskob05053d2016-01-22 16:12:06 +0100325 opts->authkeys[opts->authkey_count - 1].path = lydict_insert(server_opts.ctx, pubkey_path, 0);
326 opts->authkeys[opts->authkey_count - 1].username = lydict_insert(server_opts.ctx, username, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +0100327
Michal Vasko086311b2016-01-08 09:53:11 +0100328 return 0;
329}
330
331API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100332nc_server_ssh_endpt_add_authkey(const char *endpt_name, const char *pubkey_path, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100333{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100334 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100335 struct nc_endpt *endpt;
336
Michal Vasko51e514d2016-02-02 15:51:52 +0100337 /* LOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100338 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100339 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100340 return -1;
341 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100342 ret = nc_server_ssh_add_authkey(pubkey_path, username, endpt->ti_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100343 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100344 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100345
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100346 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100347}
348
349API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100350nc_server_ssh_ch_add_authkey(const char *pubkey_path, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100351{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100352 int ret;
353
354 /* OPTS LOCK */
355 pthread_mutex_lock(&ssh_ch_opts_lock);
356 ret = nc_server_ssh_add_authkey(pubkey_path, username, &ssh_ch_opts);
357 /* OPTS UNLOCK */
358 pthread_mutex_unlock(&ssh_ch_opts_lock);
359
360 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100361}
362
363static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100364nc_server_ssh_del_authkey(const char *pubkey_path, const char *username, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100365{
Michal Vasko086311b2016-01-08 09:53:11 +0100366 uint32_t i;
367 int ret = -1;
368
Michal Vasko1a38c862016-01-15 15:50:07 +0100369 if (!pubkey_path && !username) {
Michal Vaskob05053d2016-01-22 16:12:06 +0100370 for (i = 0; i < opts->authkey_count; ++i) {
371 lydict_remove(server_opts.ctx, opts->authkeys[i].path);
372 lydict_remove(server_opts.ctx, opts->authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100373
Michal Vasko086311b2016-01-08 09:53:11 +0100374 ret = 0;
375 }
Michal Vaskob05053d2016-01-22 16:12:06 +0100376 free(opts->authkeys);
377 opts->authkeys = NULL;
378 opts->authkey_count = 0;
Michal Vasko1a38c862016-01-15 15:50:07 +0100379 } else {
Michal Vaskob05053d2016-01-22 16:12:06 +0100380 for (i = 0; i < opts->authkey_count; ++i) {
381 if ((!pubkey_path || !strcmp(opts->authkeys[i].path, pubkey_path))
382 && (!username || !strcmp(opts->authkeys[i].username, username))) {
Michal Vaskob05053d2016-01-22 16:12:06 +0100383 lydict_remove(server_opts.ctx, opts->authkeys[i].path);
384 lydict_remove(server_opts.ctx, opts->authkeys[i].username);
Michal Vasko1a38c862016-01-15 15:50:07 +0100385
Michal Vaskob05053d2016-01-22 16:12:06 +0100386 --opts->authkey_count;
Michal Vaskoc0256492016-02-02 12:19:06 +0100387 if (i < opts->authkey_count) {
388 memcpy(&opts->authkeys[i], &opts->authkeys[opts->authkey_count], sizeof *opts->authkeys);
389 } else if (!opts->authkey_count) {
390 free(opts->authkeys);
391 opts->authkeys = NULL;
392 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100393
394 ret = 0;
395 }
396 }
Michal Vasko086311b2016-01-08 09:53:11 +0100397 }
398
399 return ret;
400}
401
Michal Vaskob05053d2016-01-22 16:12:06 +0100402API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100403nc_server_ssh_endpt_del_authkey(const char *endpt_name, const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100404{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100405 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100406 struct nc_endpt *endpt;
407
Michal Vasko51e514d2016-02-02 15:51:52 +0100408 /* LOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100409 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +0100410 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100411 return -1;
412 }
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100413 ret = nc_server_ssh_del_authkey(pubkey_path, username, endpt->ti_opts);
Michal Vasko51e514d2016-02-02 15:51:52 +0100414 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100415 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100416
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100417 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100418}
419
420API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100421nc_server_ssh_ch_del_authkey(const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100422{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100423 int ret;
424
425 /* OPTS LOCK */
426 pthread_mutex_lock(&ssh_ch_opts_lock);
427 ret = nc_server_ssh_del_authkey(pubkey_path, username, &ssh_ch_opts);
428 /* OPTS UNLOCK */
429 pthread_mutex_unlock(&ssh_ch_opts_lock);
430
431 return ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100432}
433
434void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100435nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100436{
437 if (opts->sshbind) {
438 ssh_bind_free(opts->sshbind);
439 opts->sshbind = NULL;
440 }
441
442 nc_server_ssh_del_authkey(NULL, NULL, opts);
Michal Vaskob05053d2016-01-22 16:12:06 +0100443}
444
Michal Vasko086311b2016-01-08 09:53:11 +0100445API void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100446nc_server_ssh_ch_clear_opts(void)
Michal Vasko086311b2016-01-08 09:53:11 +0100447{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100448 /* OPTS LOCK */
449 pthread_mutex_lock(&ssh_ch_opts_lock);
450 nc_server_ssh_clear_opts(&ssh_ch_opts);
451 /* OPTS UNLOCK */
452 pthread_mutex_unlock(&ssh_ch_opts_lock);
Michal Vasko086311b2016-01-08 09:53:11 +0100453}
454
455static char *
456auth_password_get_pwd_hash(const char *username)
457{
458 struct passwd *pwd, pwd_buf;
459 struct spwd *spwd, spwd_buf;
460 char *pass_hash = NULL, buf[256];
461
462 getpwnam_r(username, &pwd_buf, buf, 256, &pwd);
463 if (!pwd) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100464 VRB("User \"%s\" not found locally.", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100465 return NULL;
466 }
467
468 if (!strcmp(pwd->pw_passwd, "x")) {
469 getspnam_r(username, &spwd_buf, buf, 256, &spwd);
470 if (!spwd) {
471 VRB("Failed to retrieve the shadow entry for \"%s\".", username);
472 return NULL;
473 }
474
475 pass_hash = spwd->sp_pwdp;
476 } else {
477 pass_hash = pwd->pw_passwd;
478 }
479
480 if (!pass_hash) {
Michal Vaskod083db62016-01-19 10:31:29 +0100481 ERR("No password could be retrieved for \"%s\".", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100482 return NULL;
483 }
484
485 /* check the hash structure for special meaning */
486 if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) {
487 VRB("User \"%s\" is not allowed to authenticate using a password.", username);
488 return NULL;
489 }
490 if (!strcmp(pass_hash, "*NP*")) {
491 VRB("Retrieving password for \"%s\" from a NIS+ server not supported.", username);
492 return NULL;
493 }
494
495 return strdup(pass_hash);
496}
497
498static int
499auth_password_compare_pwd(const char *pass_hash, const char *pass_clear)
500{
501 char *new_pass_hash;
502 struct crypt_data cdata;
503
504 if (!pass_hash[0]) {
505 if (!pass_clear[0]) {
506 WRN("User authentication successful with an empty password!");
507 return 0;
508 } else {
509 /* the user did now know he does not need any password,
510 * (which should not be used) so deny authentication */
511 return 1;
512 }
513 }
514
515 cdata.initialized = 0;
516 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
517 return strcmp(new_pass_hash, pass_hash);
518}
519
520static void
521nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
522{
523 char *pass_hash;
524
525 pass_hash = auth_password_get_pwd_hash(session->username);
526 if (pass_hash && !auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100527 VRB("User \"%s\" authenticated.", session->username);
Michal Vasko086311b2016-01-08 09:53:11 +0100528 ssh_message_auth_reply_success(msg, 0);
529 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
530 free(pass_hash);
531 return;
532 }
533
534 free(pass_hash);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100535 ++session->ssh_auth_attempts;
Michal Vaskod083db62016-01-19 10:31:29 +0100536 VRB("Failed user \"'%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100537 ssh_message_reply_default(msg);
538}
539
540static void
541nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
542{
543 char *pass_hash;
544
545 if (!ssh_message_auth_kbdint_is_response(msg)) {
546 const char *prompts[] = {"Password: "};
547 char echo[] = {0};
548
549 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
550 } else {
551 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {
552 ssh_message_reply_default(msg);
553 return;
554 }
555 pass_hash = auth_password_get_pwd_hash(session->username);
556 if (!pass_hash) {
557 ssh_message_reply_default(msg);
558 return;
559 }
560 if (!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0))) {
561 VRB("User \"%s\" authenticated.", session->username);
562 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
563 ssh_message_auth_reply_success(msg, 0);
564 } else {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100565 ++session->ssh_auth_attempts;
566 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100567 ssh_message_reply_default(msg);
568 }
569 }
570}
571
572static const char *
Michal Vasko3031aae2016-01-27 16:07:18 +0100573auth_pubkey_compare_key(struct nc_server_ssh_opts *opts, ssh_key key)
Michal Vasko086311b2016-01-08 09:53:11 +0100574{
575 uint32_t i;
576 ssh_key pub_key;
Michal Vasko76e3a352016-01-18 09:07:00 +0100577 const char *username = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100578
Michal Vasko3031aae2016-01-27 16:07:18 +0100579 for (i = 0; i < opts->authkey_count; ++i) {
580 if (ssh_pki_import_pubkey_file(opts->authkeys[i].path, &pub_key) != SSH_OK) {
581 if (eaccess(opts->authkeys[i].path, R_OK)) {
582 WRN("Failed to import the public key \"%s\" (%s).", opts->authkeys[i].path, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100583 } else {
Michal Vaskoe5612cf2016-02-02 15:52:16 +0100584 WRN("Failed to import the public key \"%s\" (%s).", opts->authkeys[i].path, ssh_get_error(pub_key));
Michal Vasko086311b2016-01-08 09:53:11 +0100585 }
586 continue;
587 }
588
589 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
590 ssh_key_free(pub_key);
591 break;
592 }
593
594 ssh_key_free(pub_key);
595 }
596
Michal Vasko3031aae2016-01-27 16:07:18 +0100597 if (i < opts->authkey_count) {
598 username = opts->authkeys[i].username;
Michal Vasko086311b2016-01-08 09:53:11 +0100599 }
600
601 return username;
602}
603
604static void
605nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
606{
607 const char *username;
608 int signature_state;
609
610 signature_state = ssh_message_auth_publickey_state(msg);
611 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
612 VRB("User \"%s\" authenticated.", session->username);
613 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
614 ssh_message_auth_reply_success(msg, 0);
615 return;
616
617 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100618 if ((username = auth_pubkey_compare_key(session->ti_opts, ssh_message_auth_pubkey(msg))) == NULL) {
Michal Vasko086311b2016-01-08 09:53:11 +0100619 VRB("User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
620
621 } else if (strcmp(session->username, username)) {
622 VRB("User \"%s\" is not the username identified with the presented public key.", session->username);
623
624 } else {
625 /* accepting only the use of a public key */
626 ssh_message_auth_reply_pk_ok_simple(msg);
627 return;
628 }
629 }
630
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100631 ++session->ssh_auth_attempts;
632 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100633 ssh_message_reply_default(msg);
634}
635
636static int
Michal Vasko96164bf2016-01-21 15:41:58 +0100637nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100638{
Michal Vasko96164bf2016-01-21 15:41:58 +0100639 ssh_channel chan;
640
641 /* first channel request */
642 if (!session->ti.libssh.channel) {
643 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100644 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100645 return -1;
646 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100647 chan = ssh_message_channel_request_open_reply_accept(msg);
648 if (!chan) {
649 ERR("Failed to create a new SSH channel.");
650 return -1;
651 }
652 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +0100653
Michal Vasko96164bf2016-01-21 15:41:58 +0100654 /* additional channel request */
655 } else {
656 chan = ssh_message_channel_request_open_reply_accept(msg);
657 if (!chan) {
658 ERR("Session %u: failed to create a new SSH channel.", session->id);
659 return -1;
660 }
661 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +0100662 }
663
Michal Vasko086311b2016-01-08 09:53:11 +0100664 return 0;
665}
666
667static int
668nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
669{
Michal Vasko96164bf2016-01-21 15:41:58 +0100670 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +0100671
Michal Vasko96164bf2016-01-21 15:41:58 +0100672 if (strcmp(subsystem, "netconf")) {
673 WRN("Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +0100674 return -1;
675 }
676
Michal Vasko96164bf2016-01-21 15:41:58 +0100677 if (session->ti.libssh.channel == channel) {
678 /* first channel requested */
679 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
680 ERRINT;
681 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100682 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100683 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
684 ERR("Session %u: subsystem \"netconf\" requested for the second time.", session->id);
685 return -1;
686 }
687
688 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +0100689 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +0100690 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
691 new_session = calloc(1, sizeof *new_session);
692
693 /* insert the new session */
694 if (!session->ti.libssh.next) {
695 new_session->ti.libssh.next = session;
696 } else {
697 new_session->ti.libssh.next = session->ti.libssh.next;
698 }
699 session->ti.libssh.next = new_session;
700
701 new_session->status = NC_STATUS_STARTING;
702 new_session->side = NC_SERVER;
703 new_session->ti_type = NC_TI_LIBSSH;
704 new_session->ti_lock = session->ti_lock;
705 new_session->ti.libssh.channel = channel;
706 new_session->ti.libssh.session = session->ti.libssh.session;
707 new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
708 new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
709 new_session->port = session->port;
710 new_session->ctx = server_opts.ctx;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100711 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX
712 | (session->flags & NC_SESSION_CALLHOME ? NC_SESSION_CALLHOME : 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100713 }
714
715 return 0;
716}
717
Michal Vasko96164bf2016-01-21 15:41:58 +0100718int
Michal Vaskob48aa812016-01-18 14:13:09 +0100719nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +0100720{
721 const char *str_type, *str_subtype = NULL, *username;
722 int subtype, type;
723 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +0100724
725 type = ssh_message_type(msg);
726 subtype = ssh_message_subtype(msg);
727
728 switch (type) {
729 case SSH_REQUEST_AUTH:
730 str_type = "request-auth";
731 switch (subtype) {
732 case SSH_AUTH_METHOD_NONE:
733 str_subtype = "none";
734 break;
735 case SSH_AUTH_METHOD_PASSWORD:
736 str_subtype = "password";
737 break;
738 case SSH_AUTH_METHOD_PUBLICKEY:
739 str_subtype = "publickey";
740 break;
741 case SSH_AUTH_METHOD_HOSTBASED:
742 str_subtype = "hostbased";
743 break;
744 case SSH_AUTH_METHOD_INTERACTIVE:
745 str_subtype = "interactive";
746 break;
747 case SSH_AUTH_METHOD_GSSAPI_MIC:
748 str_subtype = "gssapi-mic";
749 break;
750 }
751 break;
752
753 case SSH_REQUEST_CHANNEL_OPEN:
754 str_type = "request-channel-open";
755 switch (subtype) {
756 case SSH_CHANNEL_SESSION:
757 str_subtype = "session";
758 break;
759 case SSH_CHANNEL_DIRECT_TCPIP:
760 str_subtype = "direct-tcpip";
761 break;
762 case SSH_CHANNEL_FORWARDED_TCPIP:
763 str_subtype = "forwarded-tcpip";
764 break;
765 case (int)SSH_CHANNEL_X11:
766 str_subtype = "channel-x11";
767 break;
768 case SSH_CHANNEL_UNKNOWN:
769 /* fallthrough */
770 default:
771 str_subtype = "unknown";
772 break;
773 }
774 break;
775
776 case SSH_REQUEST_CHANNEL:
777 str_type = "request-channel";
778 switch (subtype) {
779 case SSH_CHANNEL_REQUEST_PTY:
780 str_subtype = "pty";
781 break;
782 case SSH_CHANNEL_REQUEST_EXEC:
783 str_subtype = "exec";
784 break;
785 case SSH_CHANNEL_REQUEST_SHELL:
786 str_subtype = "shell";
787 break;
788 case SSH_CHANNEL_REQUEST_ENV:
789 str_subtype = "env";
790 break;
791 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
792 str_subtype = "subsystem";
793 break;
794 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
795 str_subtype = "window-change";
796 break;
797 case SSH_CHANNEL_REQUEST_X11:
798 str_subtype = "x11";
799 break;
800 case SSH_CHANNEL_REQUEST_UNKNOWN:
801 /* fallthrough */
802 default:
803 str_subtype = "unknown";
804 break;
805 }
806 break;
807
808 case SSH_REQUEST_SERVICE:
809 str_type = "request-service";
810 str_subtype = ssh_message_service_service(msg);
811 break;
812
813 case SSH_REQUEST_GLOBAL:
814 str_type = "request-global";
815 switch (subtype) {
816 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
817 str_subtype = "tcpip-forward";
818 break;
819 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
820 str_subtype = "cancel-tcpip-forward";
821 break;
822 case SSH_GLOBAL_REQUEST_UNKNOWN:
823 /* fallthrough */
824 default:
825 str_subtype = "unknown";
826 break;
827 }
828 break;
829
830 default:
831 str_type = "unknown";
832 str_subtype = "unknown";
833 break;
834 }
835
836 VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vaskoce319162016-02-03 15:33:08 +0100837 if ((session->status == NC_STATUS_CLOSING) || (session->status == NC_STATUS_INVALID)) {
838 /* "valid" situation if, for example, receiving some auth or channel request timeouted,
839 * but we got it now, during session free */
840 VRB("SSH message arrived on a %s session, the request will be denied.",
841 (session->status == NC_STATUS_CLOSING ? "closing" : "invalid"));
842 ssh_message_reply_default(msg);
843 return 0;
844 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100845 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +0100846
847 /*
848 * process known messages
849 */
850 if (type == SSH_REQUEST_AUTH) {
851 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
852 ERR("User \"%s\" authenticated, but requested another authentication.", session->username);
853 ssh_message_reply_default(msg);
854 return 0;
855 }
856
Michal Vasko3031aae2016-01-27 16:07:18 +0100857 if (session->ssh_auth_attempts >= ((struct nc_server_ssh_opts *)session->ti_opts)->auth_attempts) {
Michal Vasko086311b2016-01-08 09:53:11 +0100858 /* too many failed attempts */
859 ssh_message_reply_default(msg);
860 return 0;
861 }
862
863 /* save the username, do not let the client change it */
864 username = ssh_message_auth_user(msg);
865 if (!session->username) {
866 if (!username) {
867 ERR("Denying an auth request without a username.");
868 return 1;
869 }
870
Michal Vasko05ba9df2016-01-13 14:40:27 +0100871 session->username = lydict_insert(server_opts.ctx, username, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100872 } else if (username) {
873 if (strcmp(username, session->username)) {
874 ERR("User \"%s\" changed its username to \"%s\".", session->username, username);
875 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +0100876 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +0100877 return 1;
878 }
879 }
880
881 if (subtype == SSH_AUTH_METHOD_NONE) {
882 /* libssh will return the supported auth methods */
883 return 1;
884 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
885 nc_sshcb_auth_password(session, msg);
886 return 0;
887 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
888 nc_sshcb_auth_pubkey(session, msg);
889 return 0;
890 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
891 nc_sshcb_auth_kbdint(session, msg);
892 return 0;
893 }
894 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +0100895 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100896 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100897 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100898 }
Michal Vasko086311b2016-01-08 09:53:11 +0100899 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +0100900
Michal Vasko0df67562016-01-21 15:50:11 +0100901 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100902 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
903 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +0100904 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +0100905 } else {
906 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100907 }
908 return 0;
909 }
910 }
911
912 /* we did not process it */
913 return 1;
914}
915
Michal Vasko1a38c862016-01-15 15:50:07 +0100916/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +0100917static int
918nc_open_netconf_channel(struct nc_session *session, int timeout)
919{
Michal Vasko105bf272016-02-03 15:34:35 +0100920 int elapsed_usec = 0, ret, elapsed;
Michal Vasko086311b2016-01-08 09:53:11 +0100921
922 /* message callback is executed twice to give chance for the channel to be
923 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100924 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +0100925 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100926 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +0100927 return -1;
928 }
929
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100930 ret = nc_timedlock(session->ti_lock, timeout, NULL);
931 if (ret != 1) {
932 return ret;
933 }
934
935 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
936 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100937 ERR("Failed to receive SSH messages on a session (%s).",
938 ssh_get_error(session->ti.libssh.session));
Michal Vasko11d142a2016-01-19 15:58:24 +0100939 pthread_mutex_unlock(session->ti_lock);
Michal Vasko086311b2016-01-08 09:53:11 +0100940 return -1;
941 }
942
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100943 if (!session->ti.libssh.channel) {
944 /* we did not receive channel-open, timeout */
945 pthread_mutex_unlock(session->ti_lock);
946 return 0;
947 }
948
949 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
950 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100951 ERR("Failed to receive SSH messages on a session (%s).",
952 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100953 pthread_mutex_unlock(session->ti_lock);
954 return -1;
955 }
956 pthread_mutex_unlock(session->ti_lock);
957
958 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
959 /* we did not receive subsystem-request, timeout */
960 return 0;
961 }
962
963 return 1;
964 }
965
966 while (1) {
967 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100968 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100969 return -1;
970 }
971
Michal Vasko105bf272016-02-03 15:34:35 +0100972 elapsed = 0;
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100973 ret = nc_timedlock(session->ti_lock, timeout, &elapsed);
974 if (ret != 1) {
975 return ret;
976 }
Michal Vasko105bf272016-02-03 15:34:35 +0100977 elapsed_usec += elapsed * 1000;
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100978
979 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
980 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100981 ERR("Failed to receive SSH messages on a session (%s).",
982 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100983 pthread_mutex_unlock(session->ti_lock);
984 return -1;
985 }
986
987 pthread_mutex_unlock(session->ti_lock);
988
Michal Vasko086311b2016-01-08 09:53:11 +0100989 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100990 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +0100991 }
992
Michal Vasko105bf272016-02-03 15:34:35 +0100993 if ((timeout != -1) && (elapsed_usec / 1000 >= timeout)) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100994 /* timeout */
Michal Vasko086311b2016-01-08 09:53:11 +0100995 break;
996 }
997
Michal Vasko086311b2016-01-08 09:53:11 +0100998 usleep(NC_TIMEOUT_STEP);
Michal Vasko105bf272016-02-03 15:34:35 +0100999 elapsed_usec += NC_TIMEOUT_STEP;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001000 }
Michal Vasko086311b2016-01-08 09:53:11 +01001001
Michal Vasko1a38c862016-01-15 15:50:07 +01001002 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001003}
1004
Michal Vasko96164bf2016-01-21 15:41:58 +01001005/* ret 0 - timeout, 1 channel has data, 2 some other channel has data,
1006 * 3 status change, 4 new SSH message, 5 new NETCONF SSH channel, -1 error */
1007int
1008nc_ssh_pollin(struct nc_session *session, int *timeout)
1009{
1010 int ret, elapsed = 0;
1011 struct nc_session *new;
1012
1013 ret = nc_timedlock(session->ti_lock, *timeout, &elapsed);
1014 if (*timeout > 0) {
1015 *timeout -= elapsed;
1016 }
1017
1018 if (ret != 1) {
1019 return ret;
1020 }
1021
1022 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1023 pthread_mutex_unlock(session->ti_lock);
1024
1025 if (ret != SSH_OK) {
1026 ERR("Session %u: failed to receive SSH messages (%s).", session->id,
1027 ssh_get_error(session->ti.libssh.session));
1028 session->status = NC_STATUS_INVALID;
1029 session->term_reason = NC_SESSION_TERM_OTHER;
1030 return 3;
1031 }
1032
1033 /* new SSH message */
1034 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1035 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1036 if (session->ti.libssh.next) {
1037 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1038 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1039 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1040 /* new NETCONF SSH channel */
1041 return 5;
1042 }
1043 }
1044 }
1045
1046 /* just some SSH message */
1047 return 4;
1048 }
1049
1050 /* no new SSH message, maybe NETCONF data? */
1051 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
1052 /* not this one */
1053 if (!ret) {
1054 return 2;
1055 } else if (ret == SSH_ERROR) {
1056 ERR("Session %u: SSH channel error (%s).", session->id,
1057 ssh_get_error(session->ti.libssh.session));
1058 session->status = NC_STATUS_INVALID;
1059 session->term_reason = NC_SESSION_TERM_OTHER;
1060 return 3;
1061 } else if (ret == SSH_EOF) {
1062 ERR("Session %u: communication channel unexpectedly closed (libssh).",
1063 session->id);
1064 session->status = NC_STATUS_INVALID;
1065 session->term_reason = NC_SESSION_TERM_DROPPED;
1066 return 3;
1067 }
1068
1069 return 1;
1070}
1071
Michal Vasko3031aae2016-01-27 16:07:18 +01001072API int
1073nc_connect_callhome_ssh(const char *host, uint16_t port, int timeout, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001074{
Michal Vasko3031aae2016-01-27 16:07:18 +01001075 return nc_connect_callhome(host, port, NC_TI_LIBSSH, timeout, session);
1076}
1077
1078int
1079nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
1080{
1081 struct nc_server_ssh_opts *opts;
Michal Vasko72387da2016-02-02 15:52:41 +01001082 int libssh_auth_methods = 0, elapsed_usec = 0, ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001083
Michal Vasko3031aae2016-01-27 16:07:18 +01001084 opts = session->ti_opts;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001085
Michal Vasko086311b2016-01-08 09:53:11 +01001086 /* other transport-specific data */
1087 session->ti_type = NC_TI_LIBSSH;
1088 session->ti.libssh.session = ssh_new();
1089 if (!session->ti.libssh.session) {
Michal Vaskod083db62016-01-19 10:31:29 +01001090 ERR("Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001091 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001092 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001093 }
1094
Michal Vaskoc61c4492016-01-25 11:13:34 +01001095 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001096 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1097 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001098 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001099 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1100 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001101 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001102 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1103 }
1104 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1105
1106 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001107 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001108 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001109
Michal Vaskoc61c4492016-01-25 11:13:34 +01001110 if (ssh_bind_accept_fd(opts->sshbind, session->ti.libssh.session, sock) == SSH_ERROR) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001111 ERR("SSH failed to accept a new connection (%s).", ssh_get_error(opts->sshbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001112 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001113 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001114 }
1115
1116 if (ssh_handle_key_exchange(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001117 ERR("SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001118 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001119 }
1120
1121 /* authenticate */
1122 do {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001123 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001124 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001125 return -1;
1126 }
1127
Michal Vasko086311b2016-01-08 09:53:11 +01001128 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001129 ERR("Failed to receive SSH messages on a session (%s).",
1130 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001131 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001132 }
1133
1134 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1135 break;
1136 }
1137
1138 usleep(NC_TIMEOUT_STEP);
Michal Vasko72387da2016-02-02 15:52:41 +01001139 elapsed_usec += NC_TIMEOUT_STEP;
1140 } while ((timeout == -1) || (timeout && (elapsed_usec / 1000 < timeout)));
Michal Vasko086311b2016-01-08 09:53:11 +01001141
1142 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1143 /* timeout */
Michal Vasko1a38c862016-01-15 15:50:07 +01001144 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001145 }
1146
1147 if (timeout > 0) {
Michal Vasko72387da2016-02-02 15:52:41 +01001148 timeout -= elapsed_usec / 1000;
Michal Vasko086311b2016-01-08 09:53:11 +01001149 }
1150
1151 /* open channel */
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001152 ret = nc_open_netconf_channel(session, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001153 if (ret < 1) {
1154 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001155 }
1156
Michal Vasko96164bf2016-01-21 15:41:58 +01001157 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1158
Michal Vasko1a38c862016-01-15 15:50:07 +01001159 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001160}
1161
Michal Vasko96164bf2016-01-21 15:41:58 +01001162API int
1163nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001164{
Michal Vasko96164bf2016-01-21 15:41:58 +01001165 struct nc_session *new_session = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001166 uint16_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001167
Michal Vasko96164bf2016-01-21 15:41:58 +01001168 if (!ps || !session) {
1169 ERRARG;
1170 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001171 }
1172
Michal Vasko96164bf2016-01-21 15:41:58 +01001173 for (i = 0; i < ps->session_count; ++i) {
1174 if ((ps->sessions[i]->status == NC_STATUS_RUNNING) && (ps->sessions[i]->ti_type == NC_TI_LIBSSH)
1175 && ps->sessions[i]->ti.libssh.next) {
1176 /* an SSH session with more channels */
1177 for (new_session = ps->sessions[i]->ti.libssh.next;
1178 new_session != ps->sessions[i];
1179 new_session = new_session->ti.libssh.next) {
1180 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1181 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1182 /* we found our session */
1183 break;
1184 }
1185 }
1186 if (new_session != ps->sessions[i]) {
1187 break;
1188 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001189
Michal Vasko96164bf2016-01-21 15:41:58 +01001190 new_session = NULL;
1191 }
1192 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001193
Michal Vasko96164bf2016-01-21 15:41:58 +01001194 if (!new_session) {
1195 ERR("No session with a NETCONF SSH channel ready was found.");
1196 return -1;
1197 }
1198
1199 /* assign new SID atomically */
1200 pthread_spin_lock(&server_opts.sid_lock);
1201 new_session->id = server_opts.new_session_id++;
1202 pthread_spin_unlock(&server_opts.sid_lock);
Michal Vaskofb89d772016-01-08 12:25:35 +01001203
Michal Vasko086311b2016-01-08 09:53:11 +01001204 /* NETCONF handshake */
1205 if (nc_handshake(new_session)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001206 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001207 }
1208 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001209 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001210
Michal Vasko96164bf2016-01-21 15:41:58 +01001211 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001212}