blob: d61d10ed7d7187c893b4843e8426b7f5f4a55273 [file] [log] [blame]
Radek Krejci206fcd62015-10-07 15:42:48 +02001/**
2 * \file session.c
Michal Vasko086311b2016-01-08 09:53:11 +01003 * \author Michal Vasko <mvasko@cesnet.cz>
4 * \brief libnetconf2 - general session functions
Radek Krejci206fcd62015-10-07 15:42:48 +02005 *
Michal Vasko18aeb5d2017-02-17 09:23:56 +01006 * Copyright (c) 2015 - 2017 CESNET, z.s.p.o.
Radek Krejci206fcd62015-10-07 15:42:48 +02007 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * https://opensource.org/licenses/BSD-3-Clause
Radek Krejci206fcd62015-10-07 15:42:48 +020013 */
14
Michal Vasko18aeb5d2017-02-17 09:23:56 +010015#include <assert.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020016#include <errno.h>
Radek Krejci952eb862016-01-08 14:22:55 +010017#include <stdlib.h>
Michal Vasko3512e402016-01-28 16:22:34 +010018#include <string.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020019#include <pthread.h>
Radek Krejcid6b73e12016-07-15 12:00:23 +020020#include <sys/time.h>
Michal Vasko58f31552016-01-19 12:39:15 +010021#include <time.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020022#include <libyang/libyang.h>
23
Michal Vasko5e228792016-02-03 15:30:24 +010024#include "session.h"
Michal Vaskob48aa812016-01-18 14:13:09 +010025#include "libnetconf.h"
Michal Vasko11d142a2016-01-19 15:58:24 +010026#include "session_server.h"
Michal Vaskob48aa812016-01-18 14:13:09 +010027
Radek Krejci53691be2016-02-22 13:58:37 +010028#ifdef NC_ENABLED_SSH
Radek Krejci206fcd62015-10-07 15:42:48 +020029
Michal Vasko086311b2016-01-08 09:53:11 +010030# include <libssh/libssh.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020031
Radek Krejci53691be2016-02-22 13:58:37 +010032#endif /* NC_ENABLED_SSH */
Radek Krejci695d4fa2015-10-22 13:23:54 +020033
Radek Krejci53691be2016-02-22 13:58:37 +010034#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vaskoc14e3c82016-01-11 16:14:30 +010035
Michal Vasko5e228792016-02-03 15:30:24 +010036# include <openssl/engine.h>
37# include <openssl/conf.h>
Michal Vaskoc14e3c82016-01-11 16:14:30 +010038# include <openssl/err.h>
39
Radek Krejci53691be2016-02-22 13:58:37 +010040#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskoc14e3c82016-01-11 16:14:30 +010041
Michal Vasko086311b2016-01-08 09:53:11 +010042/* in seconds */
43#define NC_CLIENT_HELLO_TIMEOUT 60
Radek Krejci695d4fa2015-10-22 13:23:54 +020044
Michal Vasko05ba9df2016-01-13 14:40:27 +010045/* in milliseconds */
46#define NC_CLOSE_REPLY_TIMEOUT 200
47
Michal Vasko086311b2016-01-08 09:53:11 +010048extern struct nc_server_opts server_opts;
49
Radek Krejci7ac16052016-07-15 11:48:18 +020050int
51nc_gettimespec(struct timespec *ts)
52{
Michal Vasko0ef46df2016-09-21 14:03:18 +020053#ifdef CLOCK_REALTIME
Radek Krejci7ac16052016-07-15 11:48:18 +020054 return clock_gettime(CLOCK_REALTIME, ts);
55#else
56 int rc;
57 struct timeval tv;
58
59 rc = gettimeofday(&tv, NULL);
60 if (!rc) {
61 ts->tv_sec = (time_t)tv.tv_sec;
62 ts->tv_nsec = 1000L * (long)tv.tv_usec;
63 }
64 return rc;
65#endif
66}
67
Michal Vasko36c7be82017-02-22 13:37:59 +010068/* ts1 < ts2 -> +, ts1 > ts2 -> -, returns milliseconds */
69int32_t
Michal Vasko99f251b2017-01-11 11:31:46 +010070nc_difftimespec(struct timespec *ts1, struct timespec *ts2)
71{
Michal Vasko36c7be82017-02-22 13:37:59 +010072 int64_t nsec_diff = 0;
Michal Vasko99f251b2017-01-11 11:31:46 +010073
74 if (ts1->tv_nsec > ts2->tv_nsec) {
75 ts2->tv_nsec += 1000000000L;
76 --ts2->tv_sec;
77 }
78
Michal Vasko36c7be82017-02-22 13:37:59 +010079 nsec_diff += (ts2->tv_sec - ts1->tv_sec) * 1000000000L;
80 nsec_diff += ts2->tv_nsec - ts1->tv_nsec;
Michal Vasko99f251b2017-01-11 11:31:46 +010081
82 return (nsec_diff ? nsec_diff / 1000000L : 0);
83}
84
Michal Vasko36c7be82017-02-22 13:37:59 +010085void
86nc_addtimespec(struct timespec *ts, uint32_t msec)
87{
88 ts->tv_sec += msec / 1000000L;
89 ts->tv_nsec += (msec % 1000000L) * 1000000L;
90
91 if (ts->tv_nsec > 1000000000L) {
92 ts->tv_sec += ts->tv_nsec / 1000000000L;
93 ts->tv_nsec %= 1000000000L;
94 }
95}
96
Radek Krejci28472922016-07-15 11:51:16 +020097#ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK
98int
99pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abstime)
100{
101 int rc;
102 struct timespec cur, dur;
103
104 /* Try to acquire the lock and, if we fail, sleep for 5ms. */
105 while ((rc = pthread_mutex_trylock(mutex)) == EBUSY) {
106 nc_gettimespec(&cur);
107
108 if ((cur.tv_sec > abstime->tv_sec) || ((cur.tv_sec == abstime->tv_sec) && (cur.tv_nsec >= abstime->tv_nsec))) {
109 break;
110 }
111
112 dur.tv_sec = abstime->tv_sec - cur.tv_sec;
113 dur.tv_nsec = abstime->tv_nsec - cur.tv_nsec;
114 if (dur.tv_nsec < 0) {
115 dur.tv_sec--;
116 dur.tv_nsec += 1000000000;
117 }
118
119 if ((dur.tv_sec != 0) || (dur.tv_nsec > 5000000)) {
120 dur.tv_sec = 0;
121 dur.tv_nsec = 5000000;
122 }
123
124 nanosleep(&dur, NULL);
125 }
126
127 return rc;
128}
129#endif
130
Michal Vasko96164bf2016-01-21 15:41:58 +0100131/*
132 * @return 1 - success
133 * 0 - timeout
134 * -1 - error
135 */
136int
Michal vasko50cc94f2016-10-04 13:46:20 +0200137nc_timedlock(pthread_mutex_t *lock, int timeout, const char *func)
Michal Vasko96164bf2016-01-21 15:41:58 +0100138{
139 int ret;
Michal Vasko62be1ce2016-03-03 13:24:52 +0100140 struct timespec ts_timeout;
Michal Vasko96164bf2016-01-21 15:41:58 +0100141
142 if (timeout > 0) {
Radek Krejci7ac16052016-07-15 11:48:18 +0200143 nc_gettimespec(&ts_timeout);
Michal Vasko96164bf2016-01-21 15:41:58 +0100144
Michal Vasko96164bf2016-01-21 15:41:58 +0100145 ts_timeout.tv_sec += timeout / 1000;
146 ts_timeout.tv_nsec += (timeout % 1000) * 1000000;
147
148 ret = pthread_mutex_timedlock(lock, &ts_timeout);
Michal Vasko96164bf2016-01-21 15:41:58 +0100149 } else if (!timeout) {
150 ret = pthread_mutex_trylock(lock);
Michal vasko2f8e4b52016-10-05 13:04:11 +0200151 if (ret == EBUSY) {
152 /* equivalent in this case */
153 ret = ETIMEDOUT;
154 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100155 } else { /* timeout == -1 */
156 ret = pthread_mutex_lock(lock);
157 }
158
159 if (ret == ETIMEDOUT) {
160 /* timeout */
161 return 0;
162 } else if (ret) {
163 /* error */
Michal vasko50cc94f2016-10-04 13:46:20 +0200164 ERR("Mutex lock failed (%s, %s).", func, strerror(ret));
Michal Vasko96164bf2016-01-21 15:41:58 +0100165 return -1;
166 }
167
168 /* ok */
169 return 1;
170}
171
Michal Vasko8dadf782016-01-15 10:29:36 +0100172API NC_STATUS
173nc_session_get_status(const struct nc_session *session)
174{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100175 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200176 ERRARG("session");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100177 return 0;
178 }
179
Michal Vasko8dadf782016-01-15 10:29:36 +0100180 return session->status;
181}
182
Radek Krejci6e36bfb2016-12-01 21:40:16 +0100183API NC_SESSION_TERM_REASON
184nc_session_get_termreason(const struct nc_session *session)
185{
186 if (!session) {
187 ERRARG("session");
188 return 0;
189 }
190
191 return session->term_reason;
192}
193
Michal Vasko8dadf782016-01-15 10:29:36 +0100194API uint32_t
195nc_session_get_id(const struct nc_session *session)
196{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100197 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200198 ERRARG("session");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100199 return 0;
200 }
201
Michal Vasko8dadf782016-01-15 10:29:36 +0100202 return session->id;
203}
204
Michal Vasko174fe8e2016-02-17 15:38:09 +0100205API int
206nc_session_get_version(const struct nc_session *session)
207{
208 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200209 ERRARG("session");
Michal Vasko174fe8e2016-02-17 15:38:09 +0100210 return -1;
211 }
212
213 return (session->version == NC_VERSION_10 ? 0 : 1);
214}
215
Michal Vasko8dadf782016-01-15 10:29:36 +0100216API NC_TRANSPORT_IMPL
217nc_session_get_ti(const struct nc_session *session)
218{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100219 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200220 ERRARG("session");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100221 return 0;
222 }
223
Michal Vasko8dadf782016-01-15 10:29:36 +0100224 return session->ti_type;
225}
226
227API const char *
228nc_session_get_username(const struct nc_session *session)
229{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100230 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200231 ERRARG("session");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100232 return NULL;
233 }
234
Michal Vasko8dadf782016-01-15 10:29:36 +0100235 return session->username;
236}
237
238API const char *
239nc_session_get_host(const struct nc_session *session)
240{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100241 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200242 ERRARG("session");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100243 return NULL;
244 }
245
Michal Vasko8dadf782016-01-15 10:29:36 +0100246 return session->host;
247}
248
249API uint16_t
250nc_session_get_port(const struct nc_session *session)
251{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100252 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200253 ERRARG("session");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100254 return 0;
255 }
256
Michal Vasko8dadf782016-01-15 10:29:36 +0100257 return session->port;
258}
259
Michal Vasko9a25e932016-02-01 10:36:42 +0100260API struct ly_ctx *
261nc_session_get_ctx(const struct nc_session *session)
262{
263 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200264 ERRARG("session");
Michal Vasko9a25e932016-02-01 10:36:42 +0100265 return NULL;
266 }
267
268 return session->ctx;
269}
270
Michal Vasko2cc4c682016-03-01 09:16:48 +0100271API void
272nc_session_set_data(struct nc_session *session, void *data)
273{
274 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200275 ERRARG("session");
Michal Vasko2cc4c682016-03-01 09:16:48 +0100276 return;
277 }
278
279 session->data = data;
280}
281
282API void *
283nc_session_get_data(const struct nc_session *session)
284{
285 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200286 ERRARG("session");
Michal Vasko2cc4c682016-03-01 09:16:48 +0100287 return NULL;
288 }
289
290 return session->data;
291}
292
Michal Vasko086311b2016-01-08 09:53:11 +0100293NC_MSG_TYPE
294nc_send_msg(struct nc_session *session, struct lyd_node *op)
Radek Krejci695d4fa2015-10-22 13:23:54 +0200295{
Michal Vasko086311b2016-01-08 09:53:11 +0100296 int r;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200297
Michal Vasko086311b2016-01-08 09:53:11 +0100298 if (session->ctx != op->schema->module->ctx) {
Michal Vaskod083db62016-01-19 10:31:29 +0100299 ERR("Session %u: RPC \"%s\" was created in different context than that of the session.",
300 session->id, op->schema->name);
Michal Vasko086311b2016-01-08 09:53:11 +0100301 return NC_MSG_ERROR;
Michal Vasko7df39ec2015-12-09 15:26:24 +0100302 }
303
Michal Vasko086311b2016-01-08 09:53:11 +0100304 r = nc_write_msg(session, NC_MSG_RPC, op, NULL);
305
306 if (r) {
307 return NC_MSG_ERROR;
308 }
309
310 return NC_MSG_RPC;
Michal Vasko7df39ec2015-12-09 15:26:24 +0100311}
312
Radek Krejci695d4fa2015-10-22 13:23:54 +0200313API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100314nc_session_free(struct nc_session *session, void (*data_free)(void *))
Radek Krejci695d4fa2015-10-22 13:23:54 +0200315{
Michal Vasko9e99f012016-03-03 13:25:20 +0100316 int r, i, locked;
Michal Vasko428087d2016-01-14 16:04:28 +0100317 int connected; /* flag to indicate whether the transport socket is still connected */
Michal Vaskob48aa812016-01-18 14:13:09 +0100318 int multisession = 0; /* flag for more NETCONF sessions on a single SSH session */
Michal Vaskoa8ad4482016-01-28 14:25:54 +0100319 pthread_t tid;
Michal Vasko4589bbe2016-01-29 09:41:30 +0100320 struct nc_session *siter;
Michal Vaskoad611702015-12-03 13:41:51 +0100321 struct nc_msg_cont *contiter;
Michal Vaskoadd4c792015-10-26 15:36:58 +0100322 struct lyxml_elem *rpl, *child;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200323 struct lyd_node *close_rpc;
Michal Vaskoad611702015-12-03 13:41:51 +0100324 const struct lys_module *ietfnc;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200325 void *p;
326
Michal Vasko428087d2016-01-14 16:04:28 +0100327 if (!session || (session->status == NC_STATUS_CLOSING)) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200328 return;
329 }
330
Michal Vasko86d357c2016-03-11 13:46:38 +0100331 /* stop notifications loop if any */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200332 if ((session->side == NC_CLIENT) && session->opts.client.ntf_tid) {
333 tid = *session->opts.client.ntf_tid;
334 free((pthread_t *)session->opts.client.ntf_tid);
335 session->opts.client.ntf_tid = NULL;
Michal Vasko86d357c2016-03-11 13:46:38 +0100336 /* the thread now knows it should quit */
337
338 pthread_join(tid, NULL);
339 }
340
Michal Vaskoadd4c792015-10-26 15:36:58 +0100341 if (session->ti_lock) {
Michal Vaskof471fa02017-02-15 10:48:12 +0100342 r = nc_timedlock(session->ti_lock, NC_SESSION_FREE_LOCK_TIMEOUT, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100343 if (r == -1) {
Michal Vaskoadd4c792015-10-26 15:36:58 +0100344 return;
Michal Vasko9e99f012016-03-03 13:25:20 +0100345 } else if (!r) {
346 /* we failed to lock it, too bad */
347 locked = 0;
348 } else {
349 locked = 1;
Michal Vaskoadd4c792015-10-26 15:36:58 +0100350 }
Michal Vasko5ecbf8c2016-03-29 16:05:22 +0200351 } else {
352 ERRINT;
353 return;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200354 }
355
Michal Vasko9e99f012016-03-03 13:25:20 +0100356 if ((session->side == NC_CLIENT) && (session->status == NC_STATUS_RUNNING) && locked) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200357 /* cleanup message queues */
358 /* notifications */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200359 for (contiter = session->opts.client.notifs; contiter; ) {
Michal Vaskoad611702015-12-03 13:41:51 +0100360 lyxml_free(session->ctx, contiter->msg);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200361
Michal Vaskoad611702015-12-03 13:41:51 +0100362 p = contiter;
363 contiter = contiter->next;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200364 free(p);
365 }
366
367 /* rpc replies */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200368 for (contiter = session->opts.client.replies; contiter; ) {
Michal Vaskoad611702015-12-03 13:41:51 +0100369 lyxml_free(session->ctx, contiter->msg);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200370
Michal Vaskoad611702015-12-03 13:41:51 +0100371 p = contiter;
372 contiter = contiter->next;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200373 free(p);
374 }
375
376 /* send closing info to the other side */
377 ietfnc = ly_ctx_get_module(session->ctx, "ietf-netconf", NULL);
378 if (!ietfnc) {
Michal Vasko428087d2016-01-14 16:04:28 +0100379 WRN("Session %u: missing ietf-netconf schema in context, unable to send <close-session>.", session->id);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200380 } else {
381 close_rpc = lyd_new(NULL, ietfnc, "close-session");
Michal Vaskoad611702015-12-03 13:41:51 +0100382 nc_send_msg(session, close_rpc);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200383 lyd_free(close_rpc);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100384 switch (nc_read_msg_poll(session, NC_CLOSE_REPLY_TIMEOUT, &rpl)) {
Michal Vaskofad6e912015-10-26 15:37:22 +0100385 case NC_MSG_REPLY:
386 LY_TREE_FOR(rpl->child, child) {
387 if (!strcmp(child->name, "ok") && child->ns && !strcmp(child->ns->value, NC_NS_BASE)) {
388 break;
389 }
390 }
391 if (!child) {
Michal Vasko428087d2016-01-14 16:04:28 +0100392 WRN("Session %u: the reply to <close-session> was not <ok> as expected.", session->id);
Michal Vaskofad6e912015-10-26 15:37:22 +0100393 }
Michal Vaskoad611702015-12-03 13:41:51 +0100394 lyxml_free(session->ctx, rpl);
Michal Vaskofad6e912015-10-26 15:37:22 +0100395 break;
396 case NC_MSG_WOULDBLOCK:
Michal Vasko428087d2016-01-14 16:04:28 +0100397 WRN("Session %u: timeout for receiving a reply to <close-session> elapsed.", session->id);
Michal Vaskofad6e912015-10-26 15:37:22 +0100398 break;
399 case NC_MSG_ERROR:
Michal Vaskod083db62016-01-19 10:31:29 +0100400 ERR("Session %u: failed to receive a reply to <close-session>.", session->id);
Michal Vaskofad6e912015-10-26 15:37:22 +0100401 break;
402 default:
403 /* cannot happen */
404 break;
405 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200406 }
407
408 /* list of server's capabilities */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200409 if (session->opts.client.cpblts) {
410 for (i = 0; session->opts.client.cpblts[i]; i++) {
411 lydict_remove(session->ctx, session->opts.client.cpblts[i]);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200412 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200413 free(session->opts.client.cpblts);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200414 }
415 }
416
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100417 if (session->data && data_free) {
418 data_free(session->data);
419 }
420
Michal Vaskoc4bc5812016-10-13 10:59:36 +0200421 if ((session->side == NC_SERVER) && (session->flags & NC_SESSION_CALLHOME)) {
422 /* CH LOCK */
423 pthread_mutex_lock(session->opts.server.ch_lock);
424 }
425
Michal Vasko86d357c2016-03-11 13:46:38 +0100426 /* mark session for closing */
Radek Krejci695d4fa2015-10-22 13:23:54 +0200427 session->status = NC_STATUS_CLOSING;
Michal Vaskoc4bc5812016-10-13 10:59:36 +0200428
429 if ((session->side == NC_SERVER) && (session->flags & NC_SESSION_CALLHOME)) {
430 pthread_cond_signal(session->opts.server.ch_cond);
431
432 /* CH UNLOCK */
433 pthread_mutex_unlock(session->opts.server.ch_lock);
434 }
435
Michal Vasko428087d2016-01-14 16:04:28 +0100436 connected = nc_session_is_connected(session);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200437
438 /* transport implementation cleanup */
439 switch (session->ti_type) {
440 case NC_TI_FD:
441 /* nothing needed - file descriptors were provided by caller,
442 * so it is up to the caller to close them correctly
443 * TODO use callbacks
444 */
Michal Vasko3512e402016-01-28 16:22:34 +0100445 /* just to avoid compiler warning */
446 (void)connected;
Michal Vasko4589bbe2016-01-29 09:41:30 +0100447 (void)siter;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200448 break;
449
Radek Krejci53691be2016-02-22 13:58:37 +0100450#ifdef NC_ENABLED_SSH
Radek Krejci695d4fa2015-10-22 13:23:54 +0200451 case NC_TI_LIBSSH:
Michal Vasko428087d2016-01-14 16:04:28 +0100452 if (connected) {
453 ssh_channel_free(session->ti.libssh.channel);
454 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200455 /* There can be multiple NETCONF sessions on the same SSH session (NETCONF session maps to
456 * SSH channel). So destroy the SSH session only if there is no other NETCONF session using
457 * it.
458 */
Michal Vasko96164bf2016-01-21 15:41:58 +0100459 multisession = 0;
460 if (session->ti.libssh.next) {
461 for (siter = session->ti.libssh.next; siter != session; siter = siter->ti.libssh.next) {
462 if (siter->status != NC_STATUS_STARTING) {
463 multisession = 1;
464 break;
465 }
466 }
467 }
468
469 if (!multisession) {
470 /* it's not multisession yet, but we still need to free the starting sessions */
471 if (session->ti.libssh.next) {
472 do {
473 siter = session->ti.libssh.next;
474 session->ti.libssh.next = siter->ti.libssh.next;
475
476 /* free starting SSH NETCONF session (channel will be freed in ssh_free()) */
Michal Vasko96164bf2016-01-21 15:41:58 +0100477 lydict_remove(session->ctx, session->username);
478 lydict_remove(session->ctx, session->host);
Michal Vasko96164bf2016-01-21 15:41:58 +0100479 if (!(session->flags & NC_SESSION_SHAREDCTX)) {
Radek Krejci4ba285b2016-02-04 17:34:06 +0100480 ly_ctx_destroy(session->ctx, NULL);
Michal Vasko96164bf2016-01-21 15:41:58 +0100481 }
482
483 free(siter);
484 } while (session->ti.libssh.next != session);
485 }
Michal Vasko428087d2016-01-14 16:04:28 +0100486 if (connected) {
487 ssh_disconnect(session->ti.libssh.session);
488 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200489 ssh_free(session->ti.libssh.session);
490 } else {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200491 /* remove the session from the list */
492 for (siter = session->ti.libssh.next; siter->ti.libssh.next != session; siter = siter->ti.libssh.next);
Michal Vaskoaec4f212015-10-26 15:37:45 +0100493 if (session->ti.libssh.next == siter) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200494 /* there will be only one session */
495 siter->ti.libssh.next = NULL;
496 } else {
497 /* there are still multiple sessions, keep the ring list */
498 siter->ti.libssh.next = session->ti.libssh.next;
499 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100500 /* change nc_sshcb_msg() argument, we need a RUNNING session and this one will be freed */
501 if (session->flags & NC_SESSION_SSH_MSG_CB) {
502 for (siter = session->ti.libssh.next; siter->status != NC_STATUS_RUNNING; siter = siter->ti.libssh.next) {
503 if (siter->ti.libssh.next == session) {
504 ERRINT;
505 break;
506 }
507 }
508 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, siter);
509 siter->flags |= NC_SESSION_SSH_MSG_CB;
510 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200511 }
512 break;
513#endif
514
Radek Krejci53691be2016-02-22 13:58:37 +0100515#ifdef NC_ENABLED_TLS
Radek Krejci695d4fa2015-10-22 13:23:54 +0200516 case NC_TI_OPENSSL:
Michal Vasko428087d2016-01-14 16:04:28 +0100517 if (connected) {
518 SSL_shutdown(session->ti.tls);
519 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200520 SSL_free(session->ti.tls);
Michal Vasko06e22432016-01-15 10:30:06 +0100521
Michal Vasko2e6defd2016-10-07 15:48:15 +0200522 if (session->side == NC_SERVER) {
523 X509_free(session->opts.server.client_cert);
524 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200525 break;
526#endif
Michal Vasko428087d2016-01-14 16:04:28 +0100527 case NC_TI_NONE:
528 ERRINT;
529 break;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200530 }
Michal Vasko428087d2016-01-14 16:04:28 +0100531
Radek Krejciac6d3472015-10-22 15:47:18 +0200532 lydict_remove(session->ctx, session->username);
533 lydict_remove(session->ctx, session->host);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200534
535 /* final cleanup */
Michal Vaskoadd4c792015-10-26 15:36:58 +0100536 if (session->ti_lock) {
Michal Vasko9e99f012016-03-03 13:25:20 +0100537 if (locked) {
538 pthread_mutex_unlock(session->ti_lock);
539 }
Michal Vaskob48aa812016-01-18 14:13:09 +0100540 if (!multisession) {
Michal Vaskoadd4c792015-10-26 15:36:58 +0100541 pthread_mutex_destroy(session->ti_lock);
542 free(session->ti_lock);
543 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200544 }
545
546 if (!(session->flags & NC_SESSION_SHAREDCTX)) {
Radek Krejci4ba285b2016-02-04 17:34:06 +0100547 ly_ctx_destroy(session->ctx, NULL);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200548 }
549
Michal Vaskoc4bc5812016-10-13 10:59:36 +0200550 if (session->side == NC_SERVER) {
551 if (session->opts.server.ch_cond) {
552 pthread_cond_destroy(session->opts.server.ch_cond);
553 free(session->opts.server.ch_cond);
554 }
555 if (session->opts.server.ch_lock) {
556 pthread_mutex_destroy(session->opts.server.ch_lock);
557 free(session->opts.server.ch_lock);
558 }
559 }
560
Radek Krejci695d4fa2015-10-22 13:23:54 +0200561 free(session);
562}
563
Michal Vasko086311b2016-01-08 09:53:11 +0100564static void
565add_cpblt(struct ly_ctx *ctx, const char *capab, const char ***cpblts, int *size, int *count)
566{
Radek Krejci658782b2016-12-04 22:04:55 +0100567 size_t len;
568 int i;
569 char *p;
570
571 if (capab) {
572 /* check if already present */
573 p = strchr(capab, '?');
574 if (p) {
575 len = p - capab;
576 } else {
577 len = strlen(capab);
578 }
579 for (i = 0; i < *count; i++) {
580 if (!strncmp((*cpblts)[i], capab, len)) {
581 /* already present, do not duplicate it */
582 return;
583 }
584 }
585 }
586
587 /* add another capability */
Michal Vasko086311b2016-01-08 09:53:11 +0100588 if (*count == *size) {
589 *size += 5;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100590 *cpblts = nc_realloc(*cpblts, *size * sizeof **cpblts);
591 if (!(*cpblts)) {
592 ERRMEM;
593 return;
594 }
Michal Vasko086311b2016-01-08 09:53:11 +0100595 }
596
597 if (capab) {
598 (*cpblts)[*count] = lydict_insert(ctx, capab, 0);
599 } else {
600 (*cpblts)[*count] = NULL;
601 }
602 ++(*count);
603}
604
Michal Vasko4ffa3b22016-05-24 16:36:25 +0200605API const char **
606nc_server_get_cpblts(struct ly_ctx *ctx)
Michal Vasko086311b2016-01-08 09:53:11 +0100607{
608 struct lyd_node *child, *child2, *yanglib;
Michal Vaskodd9fe652016-09-14 09:24:32 +0200609 struct lyd_node_leaf_list **features = NULL, **deviations = NULL, *ns = NULL, *rev = NULL, *name = NULL, *module_set_id = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100610 const char **cpblts;
611 const struct lys_module *mod;
Michal Vaskoe90e4d12016-06-20 10:05:01 +0200612 int size = 10, count, feat_count = 0, dev_count = 0, i, str_len;
Radek Krejci658782b2016-12-04 22:04:55 +0100613 unsigned int u;
Michal Vasko2e47ef92016-06-20 10:03:24 +0200614#define NC_CPBLT_BUF_LEN 512
615 char str[NC_CPBLT_BUF_LEN];
Michal Vasko086311b2016-01-08 09:53:11 +0100616
Michal Vasko4ffa3b22016-05-24 16:36:25 +0200617 if (!ctx) {
618 ERRARG("ctx");
619 return NULL;
620 }
621
Michal Vasko086311b2016-01-08 09:53:11 +0100622 yanglib = ly_ctx_info(ctx);
623 if (!yanglib) {
Michal Vasko4ffa3b22016-05-24 16:36:25 +0200624 ERR("Failed to get ietf-yang-library data from the context.");
Michal Vasko086311b2016-01-08 09:53:11 +0100625 return NULL;
626 }
627
628 cpblts = malloc(size * sizeof *cpblts);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100629 if (!cpblts) {
630 ERRMEM;
631 return NULL;
632 }
Michal Vasko086311b2016-01-08 09:53:11 +0100633 cpblts[0] = lydict_insert(ctx, "urn:ietf:params:netconf:base:1.0", 0);
634 cpblts[1] = lydict_insert(ctx, "urn:ietf:params:netconf:base:1.1", 0);
635 count = 2;
636
637 /* capabilities */
638
639 mod = ly_ctx_get_module(ctx, "ietf-netconf", NULL);
640 if (mod) {
641 if (lys_features_state(mod, "writable-running") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100642 add_cpblt(ctx, "urn:ietf:params:netconf:capability:writable-running:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100643 }
644 if (lys_features_state(mod, "candidate") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100645 add_cpblt(ctx, "urn:ietf:params:netconf:capability:candidate:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100646 if (lys_features_state(mod, "confirmed-commit") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100647 add_cpblt(ctx, "urn:ietf:params:netconf:capability:confirmed-commit:1.1", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100648 }
649 }
650 if (lys_features_state(mod, "rollback-on-error") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100651 add_cpblt(ctx, "urn:ietf:params:netconf:capability:rollback-on-error:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100652 }
653 if (lys_features_state(mod, "validate") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100654 add_cpblt(ctx, "urn:ietf:params:netconf:capability:validate:1.1", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100655 }
656 if (lys_features_state(mod, "startup") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100657 add_cpblt(ctx, "urn:ietf:params:netconf:capability:startup:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100658 }
659 if (lys_features_state(mod, "url") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100660 add_cpblt(ctx, "urn:ietf:params:netconf:capability:url:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100661 }
662 if (lys_features_state(mod, "xpath") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100663 add_cpblt(ctx, "urn:ietf:params:netconf:capability:xpath:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100664 }
665 }
666
667 mod = ly_ctx_get_module(ctx, "ietf-netconf-with-defaults", NULL);
668 if (mod) {
669 if (!server_opts.wd_basic_mode) {
670 VRB("with-defaults capability will not be advertised even though \"ietf-netconf-with-defaults\" model is present, unknown basic-mode.");
671 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +0100672 strcpy(str, "urn:ietf:params:netconf:capability:with-defaults:1.0");
Michal Vasko086311b2016-01-08 09:53:11 +0100673 switch (server_opts.wd_basic_mode) {
674 case NC_WD_ALL:
675 strcat(str, "?basic-mode=report-all");
676 break;
677 case NC_WD_TRIM:
678 strcat(str, "?basic-mode=trim");
679 break;
680 case NC_WD_EXPLICIT:
681 strcat(str, "?basic-mode=explicit");
682 break;
683 default:
Michal Vasko9e036d52016-01-08 10:49:26 +0100684 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100685 break;
686 }
687
688 if (server_opts.wd_also_supported) {
Michal Vasko2e47ef92016-06-20 10:03:24 +0200689 strcat(str, "&also-supported=");
Michal Vasko086311b2016-01-08 09:53:11 +0100690 if (server_opts.wd_also_supported & NC_WD_ALL) {
691 strcat(str, "report-all,");
692 }
693 if (server_opts.wd_also_supported & NC_WD_ALL_TAG) {
694 strcat(str, "report-all-tagged,");
695 }
696 if (server_opts.wd_also_supported & NC_WD_TRIM) {
697 strcat(str, "trim,");
698 }
699 if (server_opts.wd_also_supported & NC_WD_EXPLICIT) {
700 strcat(str, "explicit,");
701 }
702 str[strlen(str) - 1] = '\0';
703
704 add_cpblt(ctx, str, &cpblts, &size, &count);
705 }
706 }
707 }
708
Radek Krejci658782b2016-12-04 22:04:55 +0100709 /* other capabilities */
710 for (u = 0; u < server_opts.capabilities_count; u++) {
711 add_cpblt(ctx, server_opts.capabilities[u], &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100712 }
713
714 /* models */
Michal Vasko086311b2016-01-08 09:53:11 +0100715 LY_TREE_FOR(yanglib->child, child) {
Michal Vaskodd9fe652016-09-14 09:24:32 +0200716 if (!module_set_id) {
717 if (strcmp(child->prev->schema->name, "module-set-id")) {
718 ERRINT;
Michal Vaskoa8a66b62016-10-05 14:14:19 +0200719 free(cpblts);
720 free(deviations);
Michal Vaskodd9fe652016-09-14 09:24:32 +0200721 return NULL;
722 }
723 module_set_id = (struct lyd_node_leaf_list *)child->prev;
724 }
Michal Vasko086311b2016-01-08 09:53:11 +0100725 if (!strcmp(child->schema->name, "module")) {
726 LY_TREE_FOR(child->child, child2) {
727 if (!strcmp(child2->schema->name, "namespace")) {
728 ns = (struct lyd_node_leaf_list *)child2;
729 } else if (!strcmp(child2->schema->name, "name")) {
730 name = (struct lyd_node_leaf_list *)child2;
731 } else if (!strcmp(child2->schema->name, "revision")) {
732 rev = (struct lyd_node_leaf_list *)child2;
733 } else if (!strcmp(child2->schema->name, "feature")) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100734 features = nc_realloc(features, ++feat_count * sizeof *features);
735 if (!features) {
736 ERRMEM;
737 free(cpblts);
Michal Vaskoe90e4d12016-06-20 10:05:01 +0200738 free(deviations);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100739 return NULL;
740 }
Michal Vasko086311b2016-01-08 09:53:11 +0100741 features[feat_count - 1] = (struct lyd_node_leaf_list *)child2;
Michal Vaskoe90e4d12016-06-20 10:05:01 +0200742 } else if (!strcmp(child2->schema->name, "deviation")) {
743 deviations = nc_realloc(deviations, ++dev_count * sizeof *deviations);
744 if (!deviations) {
745 ERRMEM;
746 free(cpblts);
747 free(features);
748 return NULL;
749 }
Michal Vasko086311b2016-01-08 09:53:11 +0100750 }
751 }
752
753 if (!ns || !name || !rev) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100754 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100755 continue;
756 }
757
Radek Krejcidf7ba522016-03-01 16:05:25 +0100758 str_len = sprintf(str, "%s?module=%s%s%s", ns->value_str, name->value_str,
Michal Vasko2e47ef92016-06-20 10:03:24 +0200759 rev->value_str[0] ? "&revision=" : "", rev->value_str);
Michal Vasko086311b2016-01-08 09:53:11 +0100760 if (feat_count) {
Michal Vasko2e47ef92016-06-20 10:03:24 +0200761 strcat(str, "&features=");
762 str_len += 10;
Michal Vasko086311b2016-01-08 09:53:11 +0100763 for (i = 0; i < feat_count; ++i) {
Michal Vasko2e47ef92016-06-20 10:03:24 +0200764 if (str_len + 1 + strlen(features[i]->value_str) >= NC_CPBLT_BUF_LEN) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100765 ERRINT;
766 break;
767 }
Michal Vasko086311b2016-01-08 09:53:11 +0100768 if (i) {
769 strcat(str, ",");
Michal Vasko11d142a2016-01-19 15:58:24 +0100770 ++str_len;
Michal Vasko086311b2016-01-08 09:53:11 +0100771 }
772 strcat(str, features[i]->value_str);
Michal Vasko11d142a2016-01-19 15:58:24 +0100773 str_len += strlen(features[i]->value_str);
Michal Vasko086311b2016-01-08 09:53:11 +0100774 }
775 }
Michal Vaskoe90e4d12016-06-20 10:05:01 +0200776 if (dev_count) {
777 strcat(str, "&deviations=");
778 str_len += 12;
779 for (i = 0; i < dev_count; ++i) {
780 if (str_len + 1 + strlen(deviations[i]->value_str) >= NC_CPBLT_BUF_LEN) {
781 ERRINT;
782 break;
783 }
784 if (i) {
785 strcat(str, ",");
786 ++str_len;
787 }
788 strcat(str, deviations[i]->value_str);
789 str_len += strlen(deviations[i]->value_str);
790 }
791 }
Michal Vaskodd9fe652016-09-14 09:24:32 +0200792 if (!strcmp(name->value_str, "ietf-yang-library")) {
793 str_len += sprintf(str + str_len, "&module-set-id=%s", module_set_id->value_str);
794 }
Michal Vasko086311b2016-01-08 09:53:11 +0100795
796 add_cpblt(ctx, str, &cpblts, &size, &count);
797
798 ns = NULL;
799 name = NULL;
800 rev = NULL;
Michal Vaskoe90e4d12016-06-20 10:05:01 +0200801 if (features || feat_count) {
802 free(features);
803 features = NULL;
804 feat_count = 0;
805 }
806 if (deviations || dev_count) {
807 free(deviations);
808 deviations = NULL;
809 dev_count = 0;
810 }
Michal Vasko086311b2016-01-08 09:53:11 +0100811 }
812 }
813
814 lyd_free(yanglib);
815
816 /* ending NULL capability */
817 add_cpblt(ctx, NULL, &cpblts, &size, &count);
818
819 return cpblts;
820}
821
Radek Krejci695d4fa2015-10-22 13:23:54 +0200822static int
823parse_cpblts(struct lyxml_elem *xml, const char ***list)
824{
825 struct lyxml_elem *cpblt;
826 int ver = -1;
827 int i = 0;
828
829 if (list) {
830 /* get the storage for server's capabilities */
831 LY_TREE_FOR(xml->child, cpblt) {
832 i++;
833 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100834 /* last item remains NULL */
835 *list = calloc(i + 1, sizeof **list);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200836 if (!*list) {
837 ERRMEM;
838 return -1;
839 }
840 i = 0;
841 }
842
843 LY_TREE_FOR(xml->child, cpblt) {
844 if (strcmp(cpblt->name, "capability") && cpblt->ns && cpblt->ns->value &&
845 !strcmp(cpblt->ns->value, NC_NS_BASE)) {
846 ERR("Unexpected <%s> element in client's <hello>.", cpblt->name);
847 return -1;
848 } else if (!cpblt->ns || !cpblt->ns->value || strcmp(cpblt->ns->value, NC_NS_BASE)) {
849 continue;
850 }
851
852 /* detect NETCONF version */
853 if (ver < 0 && !strcmp(cpblt->content, "urn:ietf:params:netconf:base:1.0")) {
854 ver = 0;
855 } else if (ver < 1 && !strcmp(cpblt->content, "urn:ietf:params:netconf:base:1.1")) {
856 ver = 1;
857 }
858
859 /* store capabilities */
860 if (list) {
861 (*list)[i] = cpblt->content;
862 cpblt->content = NULL;
863 i++;
864 }
865 }
866
867 if (ver == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100868 ERR("Peer does not support a compatible NETCONF version.");
Radek Krejci695d4fa2015-10-22 13:23:54 +0200869 }
870
871 return ver;
872}
873
874static NC_MSG_TYPE
Michal Vasko11d142a2016-01-19 15:58:24 +0100875nc_send_client_hello(struct nc_session *session)
Michal Vasko086311b2016-01-08 09:53:11 +0100876{
877 int r, i;
878 const char **cpblts;
879
Michal Vasko11d142a2016-01-19 15:58:24 +0100880 /* client side hello - send only NETCONF base capabilities */
881 cpblts = malloc(3 * sizeof *cpblts);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100882 if (!cpblts) {
883 ERRMEM;
884 return NC_MSG_ERROR;
885 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100886 cpblts[0] = lydict_insert(session->ctx, "urn:ietf:params:netconf:base:1.0", 0);
887 cpblts[1] = lydict_insert(session->ctx, "urn:ietf:params:netconf:base:1.1", 0);
888 cpblts[2] = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100889
Michal Vasko11d142a2016-01-19 15:58:24 +0100890 r = nc_write_msg(session, NC_MSG_HELLO, cpblts, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +0100891
Michal Vasko086311b2016-01-08 09:53:11 +0100892 for (i = 0; cpblts[i]; ++i) {
893 lydict_remove(session->ctx, cpblts[i]);
894 }
895 free(cpblts);
896
897 if (r) {
898 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100899 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100900
901 return NC_MSG_HELLO;
Michal Vasko086311b2016-01-08 09:53:11 +0100902}
903
904static NC_MSG_TYPE
Michal Vasko11d142a2016-01-19 15:58:24 +0100905nc_send_server_hello(struct nc_session *session)
906{
907 int r, i;
908 const char **cpblts;
909
Michal Vasko4ffa3b22016-05-24 16:36:25 +0200910 cpblts = nc_server_get_cpblts(session->ctx);
Michal Vasko11d142a2016-01-19 15:58:24 +0100911
912 r = nc_write_msg(session, NC_MSG_HELLO, cpblts, &session->id);
913
Michal Vasko11d142a2016-01-19 15:58:24 +0100914 for (i = 0; cpblts[i]; ++i) {
915 lydict_remove(session->ctx, cpblts[i]);
916 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100917 free(cpblts);
918
919 if (r) {
920 return NC_MSG_ERROR;
921 }
922
923 return NC_MSG_HELLO;
924}
925
926static NC_MSG_TYPE
927nc_recv_client_hello(struct nc_session *session)
Radek Krejci695d4fa2015-10-22 13:23:54 +0200928{
929 struct lyxml_elem *xml = NULL, *node;
930 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
931 int ver = -1;
932 char *str;
933 long long int id;
934 int flag = 0;
935
Michal Vasko05ba9df2016-01-13 14:40:27 +0100936 msgtype = nc_read_msg_poll(session, NC_CLIENT_HELLO_TIMEOUT * 1000, &xml);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200937
938 switch(msgtype) {
939 case NC_MSG_HELLO:
940 /* parse <hello> data */
Michal Vasko11d142a2016-01-19 15:58:24 +0100941 LY_TREE_FOR(xml->child, node) {
942 if (!node->ns || !node->ns->value || strcmp(node->ns->value, NC_NS_BASE)) {
943 continue;
944 } else if (!strcmp(node->name, "session-id")) {
945 if (!node->content || !strlen(node->content)) {
946 ERR("No value of <session-id> element in server's <hello>.");
Radek Krejci695d4fa2015-10-22 13:23:54 +0200947 goto error;
948 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100949 str = NULL;
950 id = strtoll(node->content, &str, 10);
951 if (*str || id < 1 || id > UINT32_MAX) {
952 ERR("Invalid value of <session-id> element in server's <hello>.");
Radek Krejci695d4fa2015-10-22 13:23:54 +0200953 goto error;
954 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100955 session->id = (uint32_t)id;
956 continue;
957 } else if (strcmp(node->name, "capabilities")) {
958 ERR("Unexpected <%s> element in client's <hello>.", node->name);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200959 goto error;
960 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100961
962 if (flag) {
963 /* multiple capabilities elements */
964 ERR("Invalid <hello> message (multiple <capabilities> elements).");
965 goto error;
966 }
967 flag = 1;
968
Michal Vasko2e6defd2016-10-07 15:48:15 +0200969 if ((ver = parse_cpblts(node, &session->opts.client.cpblts)) < 0) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100970 goto error;
971 }
972 session->version = ver;
973 }
974
975 if (!session->id) {
976 ERR("Missing <session-id> in server's <hello>.");
977 goto error;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200978 }
979 break;
Michal Vasko71090fc2016-05-24 16:37:28 +0200980 case NC_MSG_WOULDBLOCK:
981 ERR("Server's <hello> timeout elapsed.");
982 break;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200983 case NC_MSG_ERROR:
984 /* nothing special, just pass it out */
985 break;
986 default:
987 ERR("Unexpected message received instead of <hello>.");
988 msgtype = NC_MSG_ERROR;
Michal Vasko71090fc2016-05-24 16:37:28 +0200989 break;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200990 }
991
992 /* cleanup */
Michal Vaskoad611702015-12-03 13:41:51 +0100993 lyxml_free(session->ctx, xml);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200994
995 return msgtype;
996
997error:
998 /* cleanup */
Michal Vaskoad611702015-12-03 13:41:51 +0100999 lyxml_free(session->ctx, xml);
Radek Krejci695d4fa2015-10-22 13:23:54 +02001000
1001 return NC_MSG_ERROR;
Radek Krejci5686ff72015-10-09 13:33:56 +02001002}
1003
Michal Vasko11d142a2016-01-19 15:58:24 +01001004static NC_MSG_TYPE
1005nc_recv_server_hello(struct nc_session *session)
1006{
1007 struct lyxml_elem *xml = NULL, *node;
Michal Vasko71090fc2016-05-24 16:37:28 +02001008 NC_MSG_TYPE msgtype;
Michal Vasko11d142a2016-01-19 15:58:24 +01001009 int ver = -1;
1010 int flag = 0;
1011
Michal Vaskoadb850e2016-01-20 14:06:32 +01001012 msgtype = nc_read_msg_poll(session, (server_opts.hello_timeout ? server_opts.hello_timeout * 1000 : -1), &xml);
Michal Vasko11d142a2016-01-19 15:58:24 +01001013
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001014 switch (msgtype) {
Michal Vasko11d142a2016-01-19 15:58:24 +01001015 case NC_MSG_HELLO:
1016 /* get know NETCONF version */
1017 LY_TREE_FOR(xml->child, node) {
1018 if (!node->ns || !node->ns->value || strcmp(node->ns->value, NC_NS_BASE)) {
1019 continue;
1020 } else if (strcmp(node->name, "capabilities")) {
1021 ERR("Unexpected <%s> element in client's <hello>.", node->name);
Michal Vasko71090fc2016-05-24 16:37:28 +02001022 msgtype = NC_MSG_BAD_HELLO;
Michal Vasko11d142a2016-01-19 15:58:24 +01001023 goto cleanup;
1024 }
1025
1026 if (flag) {
1027 /* multiple capabilities elements */
1028 ERR("Invalid <hello> message (multiple <capabilities> elements).");
Michal Vasko71090fc2016-05-24 16:37:28 +02001029 msgtype = NC_MSG_BAD_HELLO;
Michal Vasko11d142a2016-01-19 15:58:24 +01001030 goto cleanup;
1031 }
1032 flag = 1;
1033
1034 if ((ver = parse_cpblts(node, NULL)) < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001035 msgtype = NC_MSG_BAD_HELLO;
Michal Vasko11d142a2016-01-19 15:58:24 +01001036 goto cleanup;
1037 }
1038 session->version = ver;
1039 }
1040 break;
1041 case NC_MSG_ERROR:
1042 /* nothing special, just pass it out */
1043 break;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001044 case NC_MSG_WOULDBLOCK:
1045 ERR("Client's <hello> timeout elapsed.");
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001046 break;
Michal Vasko11d142a2016-01-19 15:58:24 +01001047 default:
1048 ERR("Unexpected message received instead of <hello>.");
1049 msgtype = NC_MSG_ERROR;
Michal Vasko71090fc2016-05-24 16:37:28 +02001050 break;
Michal Vasko11d142a2016-01-19 15:58:24 +01001051 }
1052
1053cleanup:
Michal Vasko11d142a2016-01-19 15:58:24 +01001054 lyxml_free(session->ctx, xml);
Michal Vasko11d142a2016-01-19 15:58:24 +01001055
1056 return msgtype;
1057}
1058
Michal Vasko71090fc2016-05-24 16:37:28 +02001059NC_MSG_TYPE
Michal Vasko086311b2016-01-08 09:53:11 +01001060nc_handshake(struct nc_session *session)
Michal Vasko80cad7f2015-12-08 14:42:27 +01001061{
Michal Vasko086311b2016-01-08 09:53:11 +01001062 NC_MSG_TYPE type;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001063
Michal Vasko11d142a2016-01-19 15:58:24 +01001064 if (session->side == NC_CLIENT) {
1065 type = nc_send_client_hello(session);
1066 } else {
1067 type = nc_send_server_hello(session);
1068 }
1069
Michal Vasko086311b2016-01-08 09:53:11 +01001070 if (type != NC_MSG_HELLO) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001071 return type;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001072 }
1073
Michal Vasko11d142a2016-01-19 15:58:24 +01001074 if (session->side == NC_CLIENT) {
1075 type = nc_recv_client_hello(session);
1076 } else {
1077 type = nc_recv_server_hello(session);
1078 }
1079
Michal Vasko71090fc2016-05-24 16:37:28 +02001080 return type;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001081}
Michal Vasko086311b2016-01-08 09:53:11 +01001082
Radek Krejci53691be2016-02-22 13:58:37 +01001083#ifdef NC_ENABLED_SSH
Michal Vasko086311b2016-01-08 09:53:11 +01001084
Michal Vasko8f0c0282016-02-29 10:17:14 +01001085static void
Michal Vasko086311b2016-01-08 09:53:11 +01001086nc_ssh_init(void)
1087{
1088 ssh_threads_set_callbacks(ssh_threads_get_pthread());
1089 ssh_init();
Michal Vasko086311b2016-01-08 09:53:11 +01001090}
1091
Michal Vasko8f0c0282016-02-29 10:17:14 +01001092static void
Michal Vasko086311b2016-01-08 09:53:11 +01001093nc_ssh_destroy(void)
1094{
Michal Vasko8f0c0282016-02-29 10:17:14 +01001095 FIPS_mode_set(0);
Michal Vasko5e228792016-02-03 15:30:24 +01001096 ENGINE_cleanup();
1097 CONF_modules_unload(1);
Michal Vaskob6e37262016-02-25 14:49:00 +01001098 nc_thread_destroy();
Michal Vasko086311b2016-01-08 09:53:11 +01001099 ssh_finalize();
1100}
1101
Radek Krejci53691be2016-02-22 13:58:37 +01001102#endif /* NC_ENABLED_SSH */
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001103
Radek Krejci53691be2016-02-22 13:58:37 +01001104#ifdef NC_ENABLED_TLS
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001105
Michal Vasko770b4362017-02-17 14:44:18 +01001106#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0
1107
Michal Vaskof0c92c02016-01-29 09:41:45 +01001108struct CRYPTO_dynlock_value {
1109 pthread_mutex_t lock;
1110};
1111
Michal Vaskof0c92c02016-01-29 09:41:45 +01001112static struct CRYPTO_dynlock_value *
1113tls_dyn_create_func(const char *UNUSED(file), int UNUSED(line))
1114{
1115 struct CRYPTO_dynlock_value *value;
1116
1117 value = malloc(sizeof *value);
1118 if (!value) {
1119 ERRMEM;
1120 return NULL;
1121 }
1122 pthread_mutex_init(&value->lock, NULL);
1123
1124 return value;
1125}
1126
1127static void
1128tls_dyn_lock_func(int mode, struct CRYPTO_dynlock_value *l, const char *UNUSED(file), int UNUSED(line))
1129{
1130 /* mode can also be CRYPTO_READ or CRYPTO_WRITE, but all the examples
1131 * I found ignored this fact, what do I know... */
1132 if (mode & CRYPTO_LOCK) {
1133 pthread_mutex_lock(&l->lock);
1134 } else {
1135 pthread_mutex_unlock(&l->lock);
1136 }
1137}
1138
1139static void
1140tls_dyn_destroy_func(struct CRYPTO_dynlock_value *l, const char *UNUSED(file), int UNUSED(line))
1141{
1142 pthread_mutex_destroy(&l->lock);
1143 free(l);
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001144}
Michal Vasko770b4362017-02-17 14:44:18 +01001145#endif
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001146
Michal Vasko8f0c0282016-02-29 10:17:14 +01001147#endif /* NC_ENABLED_TLS */
1148
1149#if defined(NC_ENABLED_TLS) && !defined(NC_ENABLED_SSH)
1150
Michal Vasko770b4362017-02-17 14:44:18 +01001151#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0
Michal Vasko8f0c0282016-02-29 10:17:14 +01001152static pthread_mutex_t *tls_locks;
1153
1154static void
1155tls_thread_locking_func(int mode, int n, const char *UNUSED(file), int UNUSED(line))
1156{
1157 if (mode & CRYPTO_LOCK) {
1158 pthread_mutex_lock(tls_locks + n);
1159 } else {
1160 pthread_mutex_unlock(tls_locks + n);
1161 }
1162}
1163
1164static void
1165tls_thread_id_func(CRYPTO_THREADID *tid)
1166{
1167 CRYPTO_THREADID_set_numeric(tid, (unsigned long)pthread_self());
1168}
Michal Vasko770b4362017-02-17 14:44:18 +01001169#endif
Michal Vasko8f0c0282016-02-29 10:17:14 +01001170
1171static void
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001172nc_tls_init(void)
1173{
1174 int i;
1175
1176 SSL_load_error_strings();
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001177 ERR_load_BIO_strings();
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001178 SSL_library_init();
1179
Michal Vasko770b4362017-02-17 14:44:18 +01001180#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001181 tls_locks = malloc(CRYPTO_num_locks() * sizeof *tls_locks);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001182 if (!tls_locks) {
1183 ERRMEM;
1184 return;
1185 }
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001186 for (i = 0; i < CRYPTO_num_locks(); ++i) {
1187 pthread_mutex_init(tls_locks + i, NULL);
1188 }
1189
Michal Vaskof0c92c02016-01-29 09:41:45 +01001190 CRYPTO_THREADID_set_callback(tls_thread_id_func);
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001191 CRYPTO_set_locking_callback(tls_thread_locking_func);
Michal Vaskof0c92c02016-01-29 09:41:45 +01001192
1193 CRYPTO_set_dynlock_create_callback(tls_dyn_create_func);
1194 CRYPTO_set_dynlock_lock_callback(tls_dyn_lock_func);
1195 CRYPTO_set_dynlock_destroy_callback(tls_dyn_destroy_func);
Michal Vasko770b4362017-02-17 14:44:18 +01001196#endif
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001197}
1198
Michal Vasko8f0c0282016-02-29 10:17:14 +01001199static void
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001200nc_tls_destroy(void)
1201{
1202 int i;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001203
Michal Vasko8f0c0282016-02-29 10:17:14 +01001204 FIPS_mode_set(0);
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001205 CRYPTO_cleanup_all_ex_data();
Michal Vaskob6e37262016-02-25 14:49:00 +01001206 nc_thread_destroy();
Michal Vasko5e228792016-02-03 15:30:24 +01001207 EVP_cleanup();
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001208 ERR_free_strings();
Michal Vasko770b4362017-02-17 14:44:18 +01001209#if OPENSSL_VERSION_NUMBER < 0x10002000L // < 1.0.2
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001210 sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
Michal Vasko770b4362017-02-17 14:44:18 +01001211#elif OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0
1212 SSL_COMP_free_compression_methods();
Jan Kundrát47e9e1a2016-11-21 09:41:59 +01001213#endif
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001214
Michal Vasko770b4362017-02-17 14:44:18 +01001215#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0
Michal Vaskob6e37262016-02-25 14:49:00 +01001216 CRYPTO_THREADID_set_callback(NULL);
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001217 CRYPTO_set_locking_callback(NULL);
1218 for (i = 0; i < CRYPTO_num_locks(); ++i) {
1219 pthread_mutex_destroy(tls_locks + i);
1220 }
1221 free(tls_locks);
Michal Vaskof0c92c02016-01-29 09:41:45 +01001222
1223 CRYPTO_set_dynlock_create_callback(NULL);
1224 CRYPTO_set_dynlock_lock_callback(NULL);
1225 CRYPTO_set_dynlock_destroy_callback(NULL);
Michal Vasko770b4362017-02-17 14:44:18 +01001226#endif
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001227}
1228
Michal Vasko8f0c0282016-02-29 10:17:14 +01001229#endif /* NC_ENABLED_TLS && !NC_ENABLED_SSH */
Michal Vasko5e228792016-02-03 15:30:24 +01001230
Radek Krejci53691be2016-02-22 13:58:37 +01001231#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
Michal Vasko5e228792016-02-03 15:30:24 +01001232
Michal Vasko8f0c0282016-02-29 10:17:14 +01001233static void
Michal Vasko5e228792016-02-03 15:30:24 +01001234nc_ssh_tls_init(void)
1235{
1236 SSL_load_error_strings();
1237 ERR_load_BIO_strings();
1238 SSL_library_init();
1239
1240 nc_ssh_init();
1241
Michal Vasko770b4362017-02-17 14:44:18 +01001242#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0
Michal Vasko5e228792016-02-03 15:30:24 +01001243 CRYPTO_set_dynlock_create_callback(tls_dyn_create_func);
1244 CRYPTO_set_dynlock_lock_callback(tls_dyn_lock_func);
1245 CRYPTO_set_dynlock_destroy_callback(tls_dyn_destroy_func);
Michal Vasko770b4362017-02-17 14:44:18 +01001246#endif
Michal Vasko5e228792016-02-03 15:30:24 +01001247}
1248
Michal Vasko8f0c0282016-02-29 10:17:14 +01001249static void
Michal Vasko5e228792016-02-03 15:30:24 +01001250nc_ssh_tls_destroy(void)
1251{
1252 ERR_free_strings();
Michal Vasko770b4362017-02-17 14:44:18 +01001253#if OPENSSL_VERSION_NUMBER < 0x10002000L // < 1.0.2
Michal Vasko5e228792016-02-03 15:30:24 +01001254 sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
Michal Vasko770b4362017-02-17 14:44:18 +01001255#elif OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0
1256 SSL_COMP_free_compression_methods();
Jan Kundrát47e9e1a2016-11-21 09:41:59 +01001257#endif
Michal Vasko5e228792016-02-03 15:30:24 +01001258
1259 nc_ssh_destroy();
1260
Michal Vasko770b4362017-02-17 14:44:18 +01001261#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0
Michal Vasko5e228792016-02-03 15:30:24 +01001262 CRYPTO_set_dynlock_create_callback(NULL);
1263 CRYPTO_set_dynlock_lock_callback(NULL);
1264 CRYPTO_set_dynlock_destroy_callback(NULL);
Michal Vasko770b4362017-02-17 14:44:18 +01001265#endif
Michal Vasko5e228792016-02-03 15:30:24 +01001266}
1267
Radek Krejci53691be2016-02-22 13:58:37 +01001268#endif /* NC_ENABLED_SSH && NC_ENABLED_TLS */
Michal Vasko8f0c0282016-02-29 10:17:14 +01001269
1270#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
1271
1272API void
1273nc_thread_destroy(void)
1274{
Michal Vasko8f0c0282016-02-29 10:17:14 +01001275 /* caused data-races and seems not neccessary for avoiding valgrind reachable memory */
1276 //CRYPTO_cleanup_all_ex_data();
1277
Michal Vasko770b4362017-02-17 14:44:18 +01001278#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0
1279 CRYPTO_THREADID crypto_tid;
1280
Michal Vasko8f0c0282016-02-29 10:17:14 +01001281 CRYPTO_THREADID_current(&crypto_tid);
1282 ERR_remove_thread_state(&crypto_tid);
Michal Vasko770b4362017-02-17 14:44:18 +01001283#endif
Michal Vasko8f0c0282016-02-29 10:17:14 +01001284}
1285
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001286#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
1287
1288void
Michal Vasko8f0c0282016-02-29 10:17:14 +01001289nc_init(void)
1290{
1291#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
1292 nc_ssh_tls_init();
1293#elif defined(NC_ENABLED_SSH)
1294 nc_ssh_init();
1295#elif defined(NC_ENABLED_TLS)
1296 nc_tls_init();
1297#endif
1298}
1299
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001300void
Michal Vasko8f0c0282016-02-29 10:17:14 +01001301nc_destroy(void)
1302{
1303#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
1304 nc_ssh_tls_destroy();
1305#elif defined(NC_ENABLED_SSH)
1306 nc_ssh_destroy();
1307#elif defined(NC_ENABLED_TLS)
1308 nc_tls_destroy();
1309#endif
1310}