blob: 2ceb80efae1581d043d81c7fff014ae952f3a059 [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 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * https://opensource.org/licenses/BSD-3-Clause
Radek Krejci206fcd62015-10-07 15:42:48 +020013 */
14
Radek Krejci206fcd62015-10-07 15:42:48 +020015#include <errno.h>
Radek Krejci952eb862016-01-08 14:22:55 +010016#include <stdlib.h>
Michal Vasko3512e402016-01-28 16:22:34 +010017#include <string.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020018#include <pthread.h>
Radek Krejcid6b73e12016-07-15 12:00:23 +020019#include <sys/time.h>
Michal Vasko58f31552016-01-19 12:39:15 +010020#include <time.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020021#include <libyang/libyang.h>
22
Michal Vasko5e228792016-02-03 15:30:24 +010023#include "session.h"
Michal Vaskob48aa812016-01-18 14:13:09 +010024#include "libnetconf.h"
Michal Vasko11d142a2016-01-19 15:58:24 +010025#include "session_server.h"
Michal Vaskob48aa812016-01-18 14:13:09 +010026
Radek Krejci53691be2016-02-22 13:58:37 +010027#ifdef NC_ENABLED_SSH
Radek Krejci206fcd62015-10-07 15:42:48 +020028
Michal Vasko086311b2016-01-08 09:53:11 +010029# include <libssh/libssh.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020030
Radek Krejci53691be2016-02-22 13:58:37 +010031#endif /* NC_ENABLED_SSH */
Radek Krejci695d4fa2015-10-22 13:23:54 +020032
Radek Krejci53691be2016-02-22 13:58:37 +010033#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vaskoc14e3c82016-01-11 16:14:30 +010034
Michal Vasko5e228792016-02-03 15:30:24 +010035# include <openssl/engine.h>
36# include <openssl/conf.h>
Michal Vaskoc14e3c82016-01-11 16:14:30 +010037# include <openssl/err.h>
38
Radek Krejci53691be2016-02-22 13:58:37 +010039#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskoc14e3c82016-01-11 16:14:30 +010040
Michal Vasko086311b2016-01-08 09:53:11 +010041/* in seconds */
42#define NC_CLIENT_HELLO_TIMEOUT 60
Radek Krejci695d4fa2015-10-22 13:23:54 +020043
Michal Vasko05ba9df2016-01-13 14:40:27 +010044/* in milliseconds */
45#define NC_CLOSE_REPLY_TIMEOUT 200
46
Michal Vasko086311b2016-01-08 09:53:11 +010047extern struct nc_server_opts server_opts;
48
Radek Krejci7ac16052016-07-15 11:48:18 +020049int
50nc_gettimespec(struct timespec *ts)
51{
Michal Vasko0ef46df2016-09-21 14:03:18 +020052#ifdef CLOCK_REALTIME
Radek Krejci7ac16052016-07-15 11:48:18 +020053 return clock_gettime(CLOCK_REALTIME, ts);
54#else
55 int rc;
56 struct timeval tv;
57
58 rc = gettimeofday(&tv, NULL);
59 if (!rc) {
60 ts->tv_sec = (time_t)tv.tv_sec;
61 ts->tv_nsec = 1000L * (long)tv.tv_usec;
62 }
63 return rc;
64#endif
65}
66
Radek Krejci28472922016-07-15 11:51:16 +020067#ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK
68int
69pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abstime)
70{
71 int rc;
72 struct timespec cur, dur;
73
74 /* Try to acquire the lock and, if we fail, sleep for 5ms. */
75 while ((rc = pthread_mutex_trylock(mutex)) == EBUSY) {
76 nc_gettimespec(&cur);
77
78 if ((cur.tv_sec > abstime->tv_sec) || ((cur.tv_sec == abstime->tv_sec) && (cur.tv_nsec >= abstime->tv_nsec))) {
79 break;
80 }
81
82 dur.tv_sec = abstime->tv_sec - cur.tv_sec;
83 dur.tv_nsec = abstime->tv_nsec - cur.tv_nsec;
84 if (dur.tv_nsec < 0) {
85 dur.tv_sec--;
86 dur.tv_nsec += 1000000000;
87 }
88
89 if ((dur.tv_sec != 0) || (dur.tv_nsec > 5000000)) {
90 dur.tv_sec = 0;
91 dur.tv_nsec = 5000000;
92 }
93
94 nanosleep(&dur, NULL);
95 }
96
97 return rc;
98}
99#endif
100
Michal Vasko96164bf2016-01-21 15:41:58 +0100101/*
102 * @return 1 - success
103 * 0 - timeout
104 * -1 - error
105 */
106int
Michal vasko50cc94f2016-10-04 13:46:20 +0200107nc_timedlock(pthread_mutex_t *lock, int timeout, const char *func)
Michal Vasko96164bf2016-01-21 15:41:58 +0100108{
109 int ret;
Michal Vasko62be1ce2016-03-03 13:24:52 +0100110 struct timespec ts_timeout;
Michal Vasko96164bf2016-01-21 15:41:58 +0100111
112 if (timeout > 0) {
Radek Krejci7ac16052016-07-15 11:48:18 +0200113 nc_gettimespec(&ts_timeout);
Michal Vasko96164bf2016-01-21 15:41:58 +0100114
Michal Vasko96164bf2016-01-21 15:41:58 +0100115 ts_timeout.tv_sec += timeout / 1000;
116 ts_timeout.tv_nsec += (timeout % 1000) * 1000000;
117
118 ret = pthread_mutex_timedlock(lock, &ts_timeout);
Michal Vasko96164bf2016-01-21 15:41:58 +0100119 } else if (!timeout) {
120 ret = pthread_mutex_trylock(lock);
Michal vasko2f8e4b52016-10-05 13:04:11 +0200121 if (ret == EBUSY) {
122 /* equivalent in this case */
123 ret = ETIMEDOUT;
124 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100125 } else { /* timeout == -1 */
126 ret = pthread_mutex_lock(lock);
127 }
128
129 if (ret == ETIMEDOUT) {
130 /* timeout */
131 return 0;
132 } else if (ret) {
133 /* error */
Michal vasko50cc94f2016-10-04 13:46:20 +0200134 ERR("Mutex lock failed (%s, %s).", func, strerror(ret));
Michal Vasko96164bf2016-01-21 15:41:58 +0100135 return -1;
136 }
137
138 /* ok */
139 return 1;
140}
141
Michal Vasko8dadf782016-01-15 10:29:36 +0100142API NC_STATUS
143nc_session_get_status(const struct nc_session *session)
144{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100145 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200146 ERRARG("session");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100147 return 0;
148 }
149
Michal Vasko8dadf782016-01-15 10:29:36 +0100150 return session->status;
151}
152
Radek Krejci6e36bfb2016-12-01 21:40:16 +0100153API NC_SESSION_TERM_REASON
154nc_session_get_termreason(const struct nc_session *session)
155{
156 if (!session) {
157 ERRARG("session");
158 return 0;
159 }
160
161 return session->term_reason;
162}
163
Michal Vasko8dadf782016-01-15 10:29:36 +0100164API uint32_t
165nc_session_get_id(const struct nc_session *session)
166{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100167 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200168 ERRARG("session");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100169 return 0;
170 }
171
Michal Vasko8dadf782016-01-15 10:29:36 +0100172 return session->id;
173}
174
Michal Vasko174fe8e2016-02-17 15:38:09 +0100175API int
176nc_session_get_version(const struct nc_session *session)
177{
178 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200179 ERRARG("session");
Michal Vasko174fe8e2016-02-17 15:38:09 +0100180 return -1;
181 }
182
183 return (session->version == NC_VERSION_10 ? 0 : 1);
184}
185
Michal Vasko8dadf782016-01-15 10:29:36 +0100186API NC_TRANSPORT_IMPL
187nc_session_get_ti(const struct nc_session *session)
188{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100189 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200190 ERRARG("session");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100191 return 0;
192 }
193
Michal Vasko8dadf782016-01-15 10:29:36 +0100194 return session->ti_type;
195}
196
197API const char *
198nc_session_get_username(const struct nc_session *session)
199{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100200 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200201 ERRARG("session");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100202 return NULL;
203 }
204
Michal Vasko8dadf782016-01-15 10:29:36 +0100205 return session->username;
206}
207
208API const char *
209nc_session_get_host(const struct nc_session *session)
210{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100211 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200212 ERRARG("session");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100213 return NULL;
214 }
215
Michal Vasko8dadf782016-01-15 10:29:36 +0100216 return session->host;
217}
218
219API uint16_t
220nc_session_get_port(const struct nc_session *session)
221{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100222 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200223 ERRARG("session");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100224 return 0;
225 }
226
Michal Vasko8dadf782016-01-15 10:29:36 +0100227 return session->port;
228}
229
Michal Vasko9a25e932016-02-01 10:36:42 +0100230API struct ly_ctx *
231nc_session_get_ctx(const struct nc_session *session)
232{
233 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200234 ERRARG("session");
Michal Vasko9a25e932016-02-01 10:36:42 +0100235 return NULL;
236 }
237
238 return session->ctx;
239}
240
Michal Vasko2cc4c682016-03-01 09:16:48 +0100241API void
242nc_session_set_data(struct nc_session *session, void *data)
243{
244 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200245 ERRARG("session");
Michal Vasko2cc4c682016-03-01 09:16:48 +0100246 return;
247 }
248
249 session->data = data;
250}
251
252API void *
253nc_session_get_data(const struct nc_session *session)
254{
255 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200256 ERRARG("session");
Michal Vasko2cc4c682016-03-01 09:16:48 +0100257 return NULL;
258 }
259
260 return session->data;
261}
262
Michal Vasko086311b2016-01-08 09:53:11 +0100263NC_MSG_TYPE
264nc_send_msg(struct nc_session *session, struct lyd_node *op)
Radek Krejci695d4fa2015-10-22 13:23:54 +0200265{
Michal Vasko086311b2016-01-08 09:53:11 +0100266 int r;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200267
Michal Vasko086311b2016-01-08 09:53:11 +0100268 if (session->ctx != op->schema->module->ctx) {
Michal Vaskod083db62016-01-19 10:31:29 +0100269 ERR("Session %u: RPC \"%s\" was created in different context than that of the session.",
270 session->id, op->schema->name);
Michal Vasko086311b2016-01-08 09:53:11 +0100271 return NC_MSG_ERROR;
Michal Vasko7df39ec2015-12-09 15:26:24 +0100272 }
273
Michal Vasko086311b2016-01-08 09:53:11 +0100274 r = nc_write_msg(session, NC_MSG_RPC, op, NULL);
275
276 if (r) {
277 return NC_MSG_ERROR;
278 }
279
280 return NC_MSG_RPC;
Michal Vasko7df39ec2015-12-09 15:26:24 +0100281}
282
Radek Krejci695d4fa2015-10-22 13:23:54 +0200283API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100284nc_session_free(struct nc_session *session, void (*data_free)(void *))
Radek Krejci695d4fa2015-10-22 13:23:54 +0200285{
Michal Vasko9e99f012016-03-03 13:25:20 +0100286 int r, i, locked;
Michal Vasko428087d2016-01-14 16:04:28 +0100287 int connected; /* flag to indicate whether the transport socket is still connected */
Michal Vaskob48aa812016-01-18 14:13:09 +0100288 int multisession = 0; /* flag for more NETCONF sessions on a single SSH session */
Michal Vaskoa8ad4482016-01-28 14:25:54 +0100289 pthread_t tid;
Michal Vasko4589bbe2016-01-29 09:41:30 +0100290 struct nc_session *siter;
Michal Vaskoad611702015-12-03 13:41:51 +0100291 struct nc_msg_cont *contiter;
Michal Vaskoadd4c792015-10-26 15:36:58 +0100292 struct lyxml_elem *rpl, *child;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200293 struct lyd_node *close_rpc;
Michal Vaskoad611702015-12-03 13:41:51 +0100294 const struct lys_module *ietfnc;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200295 void *p;
296
Michal Vasko428087d2016-01-14 16:04:28 +0100297 if (!session || (session->status == NC_STATUS_CLOSING)) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200298 return;
299 }
300
Michal Vasko86d357c2016-03-11 13:46:38 +0100301 /* stop notifications loop if any */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200302 if ((session->side == NC_CLIENT) && session->opts.client.ntf_tid) {
303 tid = *session->opts.client.ntf_tid;
304 free((pthread_t *)session->opts.client.ntf_tid);
305 session->opts.client.ntf_tid = NULL;
Michal Vasko86d357c2016-03-11 13:46:38 +0100306 /* the thread now knows it should quit */
307
308 pthread_join(tid, NULL);
309 }
310
Michal Vaskoadd4c792015-10-26 15:36:58 +0100311 if (session->ti_lock) {
Michal vasko50cc94f2016-10-04 13:46:20 +0200312 r = nc_timedlock(session->ti_lock, NC_READ_TIMEOUT * 1000, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100313 if (r == -1) {
Michal Vaskoadd4c792015-10-26 15:36:58 +0100314 return;
Michal Vasko9e99f012016-03-03 13:25:20 +0100315 } else if (!r) {
316 /* we failed to lock it, too bad */
317 locked = 0;
318 } else {
319 locked = 1;
Michal Vaskoadd4c792015-10-26 15:36:58 +0100320 }
Michal Vasko5ecbf8c2016-03-29 16:05:22 +0200321 } else {
322 ERRINT;
323 return;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200324 }
325
Michal Vasko9e99f012016-03-03 13:25:20 +0100326 if ((session->side == NC_CLIENT) && (session->status == NC_STATUS_RUNNING) && locked) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200327 /* cleanup message queues */
328 /* notifications */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200329 for (contiter = session->opts.client.notifs; contiter; ) {
Michal Vaskoad611702015-12-03 13:41:51 +0100330 lyxml_free(session->ctx, contiter->msg);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200331
Michal Vaskoad611702015-12-03 13:41:51 +0100332 p = contiter;
333 contiter = contiter->next;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200334 free(p);
335 }
336
337 /* rpc replies */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200338 for (contiter = session->opts.client.replies; contiter; ) {
Michal Vaskoad611702015-12-03 13:41:51 +0100339 lyxml_free(session->ctx, contiter->msg);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200340
Michal Vaskoad611702015-12-03 13:41:51 +0100341 p = contiter;
342 contiter = contiter->next;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200343 free(p);
344 }
345
346 /* send closing info to the other side */
347 ietfnc = ly_ctx_get_module(session->ctx, "ietf-netconf", NULL);
348 if (!ietfnc) {
Michal Vasko428087d2016-01-14 16:04:28 +0100349 WRN("Session %u: missing ietf-netconf schema in context, unable to send <close-session>.", session->id);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200350 } else {
351 close_rpc = lyd_new(NULL, ietfnc, "close-session");
Michal Vaskoad611702015-12-03 13:41:51 +0100352 nc_send_msg(session, close_rpc);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200353 lyd_free(close_rpc);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100354 switch (nc_read_msg_poll(session, NC_CLOSE_REPLY_TIMEOUT, &rpl)) {
Michal Vaskofad6e912015-10-26 15:37:22 +0100355 case NC_MSG_REPLY:
356 LY_TREE_FOR(rpl->child, child) {
357 if (!strcmp(child->name, "ok") && child->ns && !strcmp(child->ns->value, NC_NS_BASE)) {
358 break;
359 }
360 }
361 if (!child) {
Michal Vasko428087d2016-01-14 16:04:28 +0100362 WRN("Session %u: the reply to <close-session> was not <ok> as expected.", session->id);
Michal Vaskofad6e912015-10-26 15:37:22 +0100363 }
Michal Vaskoad611702015-12-03 13:41:51 +0100364 lyxml_free(session->ctx, rpl);
Michal Vaskofad6e912015-10-26 15:37:22 +0100365 break;
366 case NC_MSG_WOULDBLOCK:
Michal Vasko428087d2016-01-14 16:04:28 +0100367 WRN("Session %u: timeout for receiving a reply to <close-session> elapsed.", session->id);
Michal Vaskofad6e912015-10-26 15:37:22 +0100368 break;
369 case NC_MSG_ERROR:
Michal Vaskod083db62016-01-19 10:31:29 +0100370 ERR("Session %u: failed to receive a reply to <close-session>.", session->id);
Michal Vaskofad6e912015-10-26 15:37:22 +0100371 break;
372 default:
373 /* cannot happen */
374 break;
375 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200376 }
377
378 /* list of server's capabilities */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200379 if (session->opts.client.cpblts) {
380 for (i = 0; session->opts.client.cpblts[i]; i++) {
381 lydict_remove(session->ctx, session->opts.client.cpblts[i]);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200382 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200383 free(session->opts.client.cpblts);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200384 }
385 }
386
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100387 if (session->data && data_free) {
388 data_free(session->data);
389 }
390
Michal Vaskoc4bc5812016-10-13 10:59:36 +0200391 if ((session->side == NC_SERVER) && (session->flags & NC_SESSION_CALLHOME)) {
392 /* CH LOCK */
393 pthread_mutex_lock(session->opts.server.ch_lock);
394 }
395
Michal Vasko86d357c2016-03-11 13:46:38 +0100396 /* mark session for closing */
Radek Krejci695d4fa2015-10-22 13:23:54 +0200397 session->status = NC_STATUS_CLOSING;
Michal Vaskoc4bc5812016-10-13 10:59:36 +0200398
399 if ((session->side == NC_SERVER) && (session->flags & NC_SESSION_CALLHOME)) {
400 pthread_cond_signal(session->opts.server.ch_cond);
401
402 /* CH UNLOCK */
403 pthread_mutex_unlock(session->opts.server.ch_lock);
404 }
405
Michal Vasko428087d2016-01-14 16:04:28 +0100406 connected = nc_session_is_connected(session);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200407
408 /* transport implementation cleanup */
409 switch (session->ti_type) {
410 case NC_TI_FD:
411 /* nothing needed - file descriptors were provided by caller,
412 * so it is up to the caller to close them correctly
413 * TODO use callbacks
414 */
Michal Vasko3512e402016-01-28 16:22:34 +0100415 /* just to avoid compiler warning */
416 (void)connected;
Michal Vasko4589bbe2016-01-29 09:41:30 +0100417 (void)siter;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200418 break;
419
Radek Krejci53691be2016-02-22 13:58:37 +0100420#ifdef NC_ENABLED_SSH
Radek Krejci695d4fa2015-10-22 13:23:54 +0200421 case NC_TI_LIBSSH:
Michal Vasko428087d2016-01-14 16:04:28 +0100422 if (connected) {
423 ssh_channel_free(session->ti.libssh.channel);
424 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200425 /* There can be multiple NETCONF sessions on the same SSH session (NETCONF session maps to
426 * SSH channel). So destroy the SSH session only if there is no other NETCONF session using
427 * it.
428 */
Michal Vasko96164bf2016-01-21 15:41:58 +0100429 multisession = 0;
430 if (session->ti.libssh.next) {
431 for (siter = session->ti.libssh.next; siter != session; siter = siter->ti.libssh.next) {
432 if (siter->status != NC_STATUS_STARTING) {
433 multisession = 1;
434 break;
435 }
436 }
437 }
438
439 if (!multisession) {
440 /* it's not multisession yet, but we still need to free the starting sessions */
441 if (session->ti.libssh.next) {
442 do {
443 siter = session->ti.libssh.next;
444 session->ti.libssh.next = siter->ti.libssh.next;
445
446 /* free starting SSH NETCONF session (channel will be freed in ssh_free()) */
Michal Vasko96164bf2016-01-21 15:41:58 +0100447 lydict_remove(session->ctx, session->username);
448 lydict_remove(session->ctx, session->host);
Michal Vasko96164bf2016-01-21 15:41:58 +0100449 if (!(session->flags & NC_SESSION_SHAREDCTX)) {
Radek Krejci4ba285b2016-02-04 17:34:06 +0100450 ly_ctx_destroy(session->ctx, NULL);
Michal Vasko96164bf2016-01-21 15:41:58 +0100451 }
452
453 free(siter);
454 } while (session->ti.libssh.next != session);
455 }
Michal Vasko428087d2016-01-14 16:04:28 +0100456 if (connected) {
457 ssh_disconnect(session->ti.libssh.session);
458 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200459 ssh_free(session->ti.libssh.session);
460 } else {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200461 /* remove the session from the list */
462 for (siter = session->ti.libssh.next; siter->ti.libssh.next != session; siter = siter->ti.libssh.next);
Michal Vaskoaec4f212015-10-26 15:37:45 +0100463 if (session->ti.libssh.next == siter) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200464 /* there will be only one session */
465 siter->ti.libssh.next = NULL;
466 } else {
467 /* there are still multiple sessions, keep the ring list */
468 siter->ti.libssh.next = session->ti.libssh.next;
469 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100470 /* change nc_sshcb_msg() argument, we need a RUNNING session and this one will be freed */
471 if (session->flags & NC_SESSION_SSH_MSG_CB) {
472 for (siter = session->ti.libssh.next; siter->status != NC_STATUS_RUNNING; siter = siter->ti.libssh.next) {
473 if (siter->ti.libssh.next == session) {
474 ERRINT;
475 break;
476 }
477 }
478 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, siter);
479 siter->flags |= NC_SESSION_SSH_MSG_CB;
480 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200481 }
482 break;
483#endif
484
Radek Krejci53691be2016-02-22 13:58:37 +0100485#ifdef NC_ENABLED_TLS
Radek Krejci695d4fa2015-10-22 13:23:54 +0200486 case NC_TI_OPENSSL:
Michal Vasko428087d2016-01-14 16:04:28 +0100487 if (connected) {
488 SSL_shutdown(session->ti.tls);
489 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200490 SSL_free(session->ti.tls);
Michal Vasko06e22432016-01-15 10:30:06 +0100491
Michal Vasko2e6defd2016-10-07 15:48:15 +0200492 if (session->side == NC_SERVER) {
493 X509_free(session->opts.server.client_cert);
494 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200495 break;
496#endif
Michal Vasko428087d2016-01-14 16:04:28 +0100497 case NC_TI_NONE:
498 ERRINT;
499 break;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200500 }
Michal Vasko428087d2016-01-14 16:04:28 +0100501
Radek Krejciac6d3472015-10-22 15:47:18 +0200502 lydict_remove(session->ctx, session->username);
503 lydict_remove(session->ctx, session->host);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200504
505 /* final cleanup */
Michal Vaskoadd4c792015-10-26 15:36:58 +0100506 if (session->ti_lock) {
Michal Vasko9e99f012016-03-03 13:25:20 +0100507 if (locked) {
508 pthread_mutex_unlock(session->ti_lock);
509 }
Michal Vaskob48aa812016-01-18 14:13:09 +0100510 if (!multisession) {
Michal Vaskoadd4c792015-10-26 15:36:58 +0100511 pthread_mutex_destroy(session->ti_lock);
512 free(session->ti_lock);
513 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200514 }
515
516 if (!(session->flags & NC_SESSION_SHAREDCTX)) {
Radek Krejci4ba285b2016-02-04 17:34:06 +0100517 ly_ctx_destroy(session->ctx, NULL);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200518 }
519
Michal Vaskoc4bc5812016-10-13 10:59:36 +0200520 if (session->side == NC_SERVER) {
521 if (session->opts.server.ch_cond) {
522 pthread_cond_destroy(session->opts.server.ch_cond);
523 free(session->opts.server.ch_cond);
524 }
525 if (session->opts.server.ch_lock) {
526 pthread_mutex_destroy(session->opts.server.ch_lock);
527 free(session->opts.server.ch_lock);
528 }
529 }
530
Radek Krejci695d4fa2015-10-22 13:23:54 +0200531 free(session);
532}
533
Michal Vasko086311b2016-01-08 09:53:11 +0100534static void
535add_cpblt(struct ly_ctx *ctx, const char *capab, const char ***cpblts, int *size, int *count)
536{
Radek Krejci658782b2016-12-04 22:04:55 +0100537 size_t len;
538 int i;
539 char *p;
540
541 if (capab) {
542 /* check if already present */
543 p = strchr(capab, '?');
544 if (p) {
545 len = p - capab;
546 } else {
547 len = strlen(capab);
548 }
549 for (i = 0; i < *count; i++) {
550 if (!strncmp((*cpblts)[i], capab, len)) {
551 /* already present, do not duplicate it */
552 return;
553 }
554 }
555 }
556
557 /* add another capability */
Michal Vasko086311b2016-01-08 09:53:11 +0100558 if (*count == *size) {
559 *size += 5;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100560 *cpblts = nc_realloc(*cpblts, *size * sizeof **cpblts);
561 if (!(*cpblts)) {
562 ERRMEM;
563 return;
564 }
Michal Vasko086311b2016-01-08 09:53:11 +0100565 }
566
567 if (capab) {
568 (*cpblts)[*count] = lydict_insert(ctx, capab, 0);
569 } else {
570 (*cpblts)[*count] = NULL;
571 }
572 ++(*count);
573}
574
Michal Vasko4ffa3b22016-05-24 16:36:25 +0200575API const char **
576nc_server_get_cpblts(struct ly_ctx *ctx)
Michal Vasko086311b2016-01-08 09:53:11 +0100577{
578 struct lyd_node *child, *child2, *yanglib;
Michal Vaskodd9fe652016-09-14 09:24:32 +0200579 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 +0100580 const char **cpblts;
581 const struct lys_module *mod;
Michal Vaskoe90e4d12016-06-20 10:05:01 +0200582 int size = 10, count, feat_count = 0, dev_count = 0, i, str_len;
Radek Krejci658782b2016-12-04 22:04:55 +0100583 unsigned int u;
Michal Vasko2e47ef92016-06-20 10:03:24 +0200584#define NC_CPBLT_BUF_LEN 512
585 char str[NC_CPBLT_BUF_LEN];
Michal Vasko086311b2016-01-08 09:53:11 +0100586
Michal Vasko4ffa3b22016-05-24 16:36:25 +0200587 if (!ctx) {
588 ERRARG("ctx");
589 return NULL;
590 }
591
Michal Vasko086311b2016-01-08 09:53:11 +0100592 yanglib = ly_ctx_info(ctx);
593 if (!yanglib) {
Michal Vasko4ffa3b22016-05-24 16:36:25 +0200594 ERR("Failed to get ietf-yang-library data from the context.");
Michal Vasko086311b2016-01-08 09:53:11 +0100595 return NULL;
596 }
597
598 cpblts = malloc(size * sizeof *cpblts);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100599 if (!cpblts) {
600 ERRMEM;
601 return NULL;
602 }
Michal Vasko086311b2016-01-08 09:53:11 +0100603 cpblts[0] = lydict_insert(ctx, "urn:ietf:params:netconf:base:1.0", 0);
604 cpblts[1] = lydict_insert(ctx, "urn:ietf:params:netconf:base:1.1", 0);
605 count = 2;
606
607 /* capabilities */
608
609 mod = ly_ctx_get_module(ctx, "ietf-netconf", NULL);
610 if (mod) {
611 if (lys_features_state(mod, "writable-running") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100612 add_cpblt(ctx, "urn:ietf:params:netconf:capability:writable-running:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100613 }
614 if (lys_features_state(mod, "candidate") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100615 add_cpblt(ctx, "urn:ietf:params:netconf:capability:candidate:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100616 if (lys_features_state(mod, "confirmed-commit") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100617 add_cpblt(ctx, "urn:ietf:params:netconf:capability:confirmed-commit:1.1", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100618 }
619 }
620 if (lys_features_state(mod, "rollback-on-error") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100621 add_cpblt(ctx, "urn:ietf:params:netconf:capability:rollback-on-error:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100622 }
623 if (lys_features_state(mod, "validate") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100624 add_cpblt(ctx, "urn:ietf:params:netconf:capability:validate:1.1", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100625 }
626 if (lys_features_state(mod, "startup") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100627 add_cpblt(ctx, "urn:ietf:params:netconf:capability:startup:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100628 }
629 if (lys_features_state(mod, "url") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100630 add_cpblt(ctx, "urn:ietf:params:netconf:capability:url:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100631 }
632 if (lys_features_state(mod, "xpath") == 1) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100633 add_cpblt(ctx, "urn:ietf:params:netconf:capability:xpath:1.0", &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100634 }
635 }
636
637 mod = ly_ctx_get_module(ctx, "ietf-netconf-with-defaults", NULL);
638 if (mod) {
639 if (!server_opts.wd_basic_mode) {
640 VRB("with-defaults capability will not be advertised even though \"ietf-netconf-with-defaults\" model is present, unknown basic-mode.");
641 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +0100642 strcpy(str, "urn:ietf:params:netconf:capability:with-defaults:1.0");
Michal Vasko086311b2016-01-08 09:53:11 +0100643 switch (server_opts.wd_basic_mode) {
644 case NC_WD_ALL:
645 strcat(str, "?basic-mode=report-all");
646 break;
647 case NC_WD_TRIM:
648 strcat(str, "?basic-mode=trim");
649 break;
650 case NC_WD_EXPLICIT:
651 strcat(str, "?basic-mode=explicit");
652 break;
653 default:
Michal Vasko9e036d52016-01-08 10:49:26 +0100654 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100655 break;
656 }
657
658 if (server_opts.wd_also_supported) {
Michal Vasko2e47ef92016-06-20 10:03:24 +0200659 strcat(str, "&also-supported=");
Michal Vasko086311b2016-01-08 09:53:11 +0100660 if (server_opts.wd_also_supported & NC_WD_ALL) {
661 strcat(str, "report-all,");
662 }
663 if (server_opts.wd_also_supported & NC_WD_ALL_TAG) {
664 strcat(str, "report-all-tagged,");
665 }
666 if (server_opts.wd_also_supported & NC_WD_TRIM) {
667 strcat(str, "trim,");
668 }
669 if (server_opts.wd_also_supported & NC_WD_EXPLICIT) {
670 strcat(str, "explicit,");
671 }
672 str[strlen(str) - 1] = '\0';
673
674 add_cpblt(ctx, str, &cpblts, &size, &count);
675 }
676 }
677 }
678
Radek Krejci658782b2016-12-04 22:04:55 +0100679 /* other capabilities */
680 for (u = 0; u < server_opts.capabilities_count; u++) {
681 add_cpblt(ctx, server_opts.capabilities[u], &cpblts, &size, &count);
Michal Vasko086311b2016-01-08 09:53:11 +0100682 }
683
684 /* models */
Michal Vasko086311b2016-01-08 09:53:11 +0100685 LY_TREE_FOR(yanglib->child, child) {
Michal Vaskodd9fe652016-09-14 09:24:32 +0200686 if (!module_set_id) {
687 if (strcmp(child->prev->schema->name, "module-set-id")) {
688 ERRINT;
Michal Vaskoa8a66b62016-10-05 14:14:19 +0200689 free(cpblts);
690 free(deviations);
Michal Vaskodd9fe652016-09-14 09:24:32 +0200691 return NULL;
692 }
693 module_set_id = (struct lyd_node_leaf_list *)child->prev;
694 }
Michal Vasko086311b2016-01-08 09:53:11 +0100695 if (!strcmp(child->schema->name, "module")) {
696 LY_TREE_FOR(child->child, child2) {
697 if (!strcmp(child2->schema->name, "namespace")) {
698 ns = (struct lyd_node_leaf_list *)child2;
699 } else if (!strcmp(child2->schema->name, "name")) {
700 name = (struct lyd_node_leaf_list *)child2;
701 } else if (!strcmp(child2->schema->name, "revision")) {
702 rev = (struct lyd_node_leaf_list *)child2;
703 } else if (!strcmp(child2->schema->name, "feature")) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100704 features = nc_realloc(features, ++feat_count * sizeof *features);
705 if (!features) {
706 ERRMEM;
707 free(cpblts);
Michal Vaskoe90e4d12016-06-20 10:05:01 +0200708 free(deviations);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100709 return NULL;
710 }
Michal Vasko086311b2016-01-08 09:53:11 +0100711 features[feat_count - 1] = (struct lyd_node_leaf_list *)child2;
Michal Vaskoe90e4d12016-06-20 10:05:01 +0200712 } else if (!strcmp(child2->schema->name, "deviation")) {
713 deviations = nc_realloc(deviations, ++dev_count * sizeof *deviations);
714 if (!deviations) {
715 ERRMEM;
716 free(cpblts);
717 free(features);
718 return NULL;
719 }
Michal Vasko086311b2016-01-08 09:53:11 +0100720 }
721 }
722
723 if (!ns || !name || !rev) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100724 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100725 continue;
726 }
727
Radek Krejcidf7ba522016-03-01 16:05:25 +0100728 str_len = sprintf(str, "%s?module=%s%s%s", ns->value_str, name->value_str,
Michal Vasko2e47ef92016-06-20 10:03:24 +0200729 rev->value_str[0] ? "&revision=" : "", rev->value_str);
Michal Vasko086311b2016-01-08 09:53:11 +0100730 if (feat_count) {
Michal Vasko2e47ef92016-06-20 10:03:24 +0200731 strcat(str, "&features=");
732 str_len += 10;
Michal Vasko086311b2016-01-08 09:53:11 +0100733 for (i = 0; i < feat_count; ++i) {
Michal Vasko2e47ef92016-06-20 10:03:24 +0200734 if (str_len + 1 + strlen(features[i]->value_str) >= NC_CPBLT_BUF_LEN) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100735 ERRINT;
736 break;
737 }
Michal Vasko086311b2016-01-08 09:53:11 +0100738 if (i) {
739 strcat(str, ",");
Michal Vasko11d142a2016-01-19 15:58:24 +0100740 ++str_len;
Michal Vasko086311b2016-01-08 09:53:11 +0100741 }
742 strcat(str, features[i]->value_str);
Michal Vasko11d142a2016-01-19 15:58:24 +0100743 str_len += strlen(features[i]->value_str);
Michal Vasko086311b2016-01-08 09:53:11 +0100744 }
745 }
Michal Vaskoe90e4d12016-06-20 10:05:01 +0200746 if (dev_count) {
747 strcat(str, "&deviations=");
748 str_len += 12;
749 for (i = 0; i < dev_count; ++i) {
750 if (str_len + 1 + strlen(deviations[i]->value_str) >= NC_CPBLT_BUF_LEN) {
751 ERRINT;
752 break;
753 }
754 if (i) {
755 strcat(str, ",");
756 ++str_len;
757 }
758 strcat(str, deviations[i]->value_str);
759 str_len += strlen(deviations[i]->value_str);
760 }
761 }
Michal Vaskodd9fe652016-09-14 09:24:32 +0200762 if (!strcmp(name->value_str, "ietf-yang-library")) {
763 str_len += sprintf(str + str_len, "&module-set-id=%s", module_set_id->value_str);
764 }
Michal Vasko086311b2016-01-08 09:53:11 +0100765
766 add_cpblt(ctx, str, &cpblts, &size, &count);
767
768 ns = NULL;
769 name = NULL;
770 rev = NULL;
Michal Vaskoe90e4d12016-06-20 10:05:01 +0200771 if (features || feat_count) {
772 free(features);
773 features = NULL;
774 feat_count = 0;
775 }
776 if (deviations || dev_count) {
777 free(deviations);
778 deviations = NULL;
779 dev_count = 0;
780 }
Michal Vasko086311b2016-01-08 09:53:11 +0100781 }
782 }
783
784 lyd_free(yanglib);
785
786 /* ending NULL capability */
787 add_cpblt(ctx, NULL, &cpblts, &size, &count);
788
789 return cpblts;
790}
791
Radek Krejci695d4fa2015-10-22 13:23:54 +0200792static int
793parse_cpblts(struct lyxml_elem *xml, const char ***list)
794{
795 struct lyxml_elem *cpblt;
796 int ver = -1;
797 int i = 0;
798
799 if (list) {
800 /* get the storage for server's capabilities */
801 LY_TREE_FOR(xml->child, cpblt) {
802 i++;
803 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100804 /* last item remains NULL */
805 *list = calloc(i + 1, sizeof **list);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200806 if (!*list) {
807 ERRMEM;
808 return -1;
809 }
810 i = 0;
811 }
812
813 LY_TREE_FOR(xml->child, cpblt) {
814 if (strcmp(cpblt->name, "capability") && cpblt->ns && cpblt->ns->value &&
815 !strcmp(cpblt->ns->value, NC_NS_BASE)) {
816 ERR("Unexpected <%s> element in client's <hello>.", cpblt->name);
817 return -1;
818 } else if (!cpblt->ns || !cpblt->ns->value || strcmp(cpblt->ns->value, NC_NS_BASE)) {
819 continue;
820 }
821
822 /* detect NETCONF version */
823 if (ver < 0 && !strcmp(cpblt->content, "urn:ietf:params:netconf:base:1.0")) {
824 ver = 0;
825 } else if (ver < 1 && !strcmp(cpblt->content, "urn:ietf:params:netconf:base:1.1")) {
826 ver = 1;
827 }
828
829 /* store capabilities */
830 if (list) {
831 (*list)[i] = cpblt->content;
832 cpblt->content = NULL;
833 i++;
834 }
835 }
836
837 if (ver == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100838 ERR("Peer does not support a compatible NETCONF version.");
Radek Krejci695d4fa2015-10-22 13:23:54 +0200839 }
840
841 return ver;
842}
843
844static NC_MSG_TYPE
Michal Vasko11d142a2016-01-19 15:58:24 +0100845nc_send_client_hello(struct nc_session *session)
Michal Vasko086311b2016-01-08 09:53:11 +0100846{
847 int r, i;
848 const char **cpblts;
849
Michal Vasko11d142a2016-01-19 15:58:24 +0100850 /* client side hello - send only NETCONF base capabilities */
851 cpblts = malloc(3 * sizeof *cpblts);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100852 if (!cpblts) {
853 ERRMEM;
854 return NC_MSG_ERROR;
855 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100856 cpblts[0] = lydict_insert(session->ctx, "urn:ietf:params:netconf:base:1.0", 0);
857 cpblts[1] = lydict_insert(session->ctx, "urn:ietf:params:netconf:base:1.1", 0);
858 cpblts[2] = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100859
Michal Vasko11d142a2016-01-19 15:58:24 +0100860 r = nc_write_msg(session, NC_MSG_HELLO, cpblts, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +0100861
Michal Vasko086311b2016-01-08 09:53:11 +0100862 for (i = 0; cpblts[i]; ++i) {
863 lydict_remove(session->ctx, cpblts[i]);
864 }
865 free(cpblts);
866
867 if (r) {
868 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100869 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100870
871 return NC_MSG_HELLO;
Michal Vasko086311b2016-01-08 09:53:11 +0100872}
873
874static NC_MSG_TYPE
Michal Vasko11d142a2016-01-19 15:58:24 +0100875nc_send_server_hello(struct nc_session *session)
876{
877 int r, i;
878 const char **cpblts;
879
Michal Vasko4ffa3b22016-05-24 16:36:25 +0200880 cpblts = nc_server_get_cpblts(session->ctx);
Michal Vasko11d142a2016-01-19 15:58:24 +0100881
882 r = nc_write_msg(session, NC_MSG_HELLO, cpblts, &session->id);
883
Michal Vasko11d142a2016-01-19 15:58:24 +0100884 for (i = 0; cpblts[i]; ++i) {
885 lydict_remove(session->ctx, cpblts[i]);
886 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100887 free(cpblts);
888
889 if (r) {
890 return NC_MSG_ERROR;
891 }
892
893 return NC_MSG_HELLO;
894}
895
896static NC_MSG_TYPE
897nc_recv_client_hello(struct nc_session *session)
Radek Krejci695d4fa2015-10-22 13:23:54 +0200898{
899 struct lyxml_elem *xml = NULL, *node;
900 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
901 int ver = -1;
902 char *str;
903 long long int id;
904 int flag = 0;
905
Michal Vasko05ba9df2016-01-13 14:40:27 +0100906 msgtype = nc_read_msg_poll(session, NC_CLIENT_HELLO_TIMEOUT * 1000, &xml);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200907
908 switch(msgtype) {
909 case NC_MSG_HELLO:
910 /* parse <hello> data */
Michal Vasko11d142a2016-01-19 15:58:24 +0100911 LY_TREE_FOR(xml->child, node) {
912 if (!node->ns || !node->ns->value || strcmp(node->ns->value, NC_NS_BASE)) {
913 continue;
914 } else if (!strcmp(node->name, "session-id")) {
915 if (!node->content || !strlen(node->content)) {
916 ERR("No value of <session-id> element in server's <hello>.");
Radek Krejci695d4fa2015-10-22 13:23:54 +0200917 goto error;
918 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100919 str = NULL;
920 id = strtoll(node->content, &str, 10);
921 if (*str || id < 1 || id > UINT32_MAX) {
922 ERR("Invalid value of <session-id> element in server's <hello>.");
Radek Krejci695d4fa2015-10-22 13:23:54 +0200923 goto error;
924 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100925 session->id = (uint32_t)id;
926 continue;
927 } else if (strcmp(node->name, "capabilities")) {
928 ERR("Unexpected <%s> element in client's <hello>.", node->name);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200929 goto error;
930 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100931
932 if (flag) {
933 /* multiple capabilities elements */
934 ERR("Invalid <hello> message (multiple <capabilities> elements).");
935 goto error;
936 }
937 flag = 1;
938
Michal Vasko2e6defd2016-10-07 15:48:15 +0200939 if ((ver = parse_cpblts(node, &session->opts.client.cpblts)) < 0) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100940 goto error;
941 }
942 session->version = ver;
943 }
944
945 if (!session->id) {
946 ERR("Missing <session-id> in server's <hello>.");
947 goto error;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200948 }
949 break;
Michal Vasko71090fc2016-05-24 16:37:28 +0200950 case NC_MSG_WOULDBLOCK:
951 ERR("Server's <hello> timeout elapsed.");
952 break;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200953 case NC_MSG_ERROR:
954 /* nothing special, just pass it out */
955 break;
956 default:
957 ERR("Unexpected message received instead of <hello>.");
958 msgtype = NC_MSG_ERROR;
Michal Vasko71090fc2016-05-24 16:37:28 +0200959 break;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200960 }
961
962 /* cleanup */
Michal Vaskoad611702015-12-03 13:41:51 +0100963 lyxml_free(session->ctx, xml);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200964
965 return msgtype;
966
967error:
968 /* cleanup */
Michal Vaskoad611702015-12-03 13:41:51 +0100969 lyxml_free(session->ctx, xml);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200970
971 return NC_MSG_ERROR;
Radek Krejci5686ff72015-10-09 13:33:56 +0200972}
973
Michal Vasko11d142a2016-01-19 15:58:24 +0100974static NC_MSG_TYPE
975nc_recv_server_hello(struct nc_session *session)
976{
977 struct lyxml_elem *xml = NULL, *node;
Michal Vasko71090fc2016-05-24 16:37:28 +0200978 NC_MSG_TYPE msgtype;
Michal Vasko11d142a2016-01-19 15:58:24 +0100979 int ver = -1;
980 int flag = 0;
981
Michal Vaskoadb850e2016-01-20 14:06:32 +0100982 msgtype = nc_read_msg_poll(session, (server_opts.hello_timeout ? server_opts.hello_timeout * 1000 : -1), &xml);
Michal Vasko11d142a2016-01-19 15:58:24 +0100983
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100984 switch (msgtype) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100985 case NC_MSG_HELLO:
986 /* get know NETCONF version */
987 LY_TREE_FOR(xml->child, node) {
988 if (!node->ns || !node->ns->value || strcmp(node->ns->value, NC_NS_BASE)) {
989 continue;
990 } else if (strcmp(node->name, "capabilities")) {
991 ERR("Unexpected <%s> element in client's <hello>.", node->name);
Michal Vasko71090fc2016-05-24 16:37:28 +0200992 msgtype = NC_MSG_BAD_HELLO;
Michal Vasko11d142a2016-01-19 15:58:24 +0100993 goto cleanup;
994 }
995
996 if (flag) {
997 /* multiple capabilities elements */
998 ERR("Invalid <hello> message (multiple <capabilities> elements).");
Michal Vasko71090fc2016-05-24 16:37:28 +0200999 msgtype = NC_MSG_BAD_HELLO;
Michal Vasko11d142a2016-01-19 15:58:24 +01001000 goto cleanup;
1001 }
1002 flag = 1;
1003
1004 if ((ver = parse_cpblts(node, NULL)) < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001005 msgtype = NC_MSG_BAD_HELLO;
Michal Vasko11d142a2016-01-19 15:58:24 +01001006 goto cleanup;
1007 }
1008 session->version = ver;
1009 }
1010 break;
1011 case NC_MSG_ERROR:
1012 /* nothing special, just pass it out */
1013 break;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001014 case NC_MSG_WOULDBLOCK:
1015 ERR("Client's <hello> timeout elapsed.");
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001016 break;
Michal Vasko11d142a2016-01-19 15:58:24 +01001017 default:
1018 ERR("Unexpected message received instead of <hello>.");
1019 msgtype = NC_MSG_ERROR;
Michal Vasko71090fc2016-05-24 16:37:28 +02001020 break;
Michal Vasko11d142a2016-01-19 15:58:24 +01001021 }
1022
1023cleanup:
Michal Vasko11d142a2016-01-19 15:58:24 +01001024 lyxml_free(session->ctx, xml);
Michal Vasko11d142a2016-01-19 15:58:24 +01001025
1026 return msgtype;
1027}
1028
Michal Vasko71090fc2016-05-24 16:37:28 +02001029NC_MSG_TYPE
Michal Vasko086311b2016-01-08 09:53:11 +01001030nc_handshake(struct nc_session *session)
Michal Vasko80cad7f2015-12-08 14:42:27 +01001031{
Michal Vasko086311b2016-01-08 09:53:11 +01001032 NC_MSG_TYPE type;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001033
Michal Vasko11d142a2016-01-19 15:58:24 +01001034 if (session->side == NC_CLIENT) {
1035 type = nc_send_client_hello(session);
1036 } else {
1037 type = nc_send_server_hello(session);
1038 }
1039
Michal Vasko086311b2016-01-08 09:53:11 +01001040 if (type != NC_MSG_HELLO) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001041 return type;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001042 }
1043
Michal Vasko11d142a2016-01-19 15:58:24 +01001044 if (session->side == NC_CLIENT) {
1045 type = nc_recv_client_hello(session);
1046 } else {
1047 type = nc_recv_server_hello(session);
1048 }
1049
Michal Vasko71090fc2016-05-24 16:37:28 +02001050 return type;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001051}
Michal Vasko086311b2016-01-08 09:53:11 +01001052
Radek Krejci53691be2016-02-22 13:58:37 +01001053#ifdef NC_ENABLED_SSH
Michal Vasko086311b2016-01-08 09:53:11 +01001054
Michal Vasko8f0c0282016-02-29 10:17:14 +01001055static void
Michal Vasko086311b2016-01-08 09:53:11 +01001056nc_ssh_init(void)
1057{
1058 ssh_threads_set_callbacks(ssh_threads_get_pthread());
1059 ssh_init();
Michal Vasko086311b2016-01-08 09:53:11 +01001060}
1061
Michal Vasko8f0c0282016-02-29 10:17:14 +01001062static void
Michal Vasko086311b2016-01-08 09:53:11 +01001063nc_ssh_destroy(void)
1064{
Michal Vasko8f0c0282016-02-29 10:17:14 +01001065 FIPS_mode_set(0);
Michal Vasko5e228792016-02-03 15:30:24 +01001066 ENGINE_cleanup();
1067 CONF_modules_unload(1);
Michal Vaskob6e37262016-02-25 14:49:00 +01001068 nc_thread_destroy();
Michal Vasko086311b2016-01-08 09:53:11 +01001069 ssh_finalize();
1070}
1071
Radek Krejci53691be2016-02-22 13:58:37 +01001072#endif /* NC_ENABLED_SSH */
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001073
Radek Krejci53691be2016-02-22 13:58:37 +01001074#ifdef NC_ENABLED_TLS
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001075
Michal Vaskof0c92c02016-01-29 09:41:45 +01001076struct CRYPTO_dynlock_value {
1077 pthread_mutex_t lock;
1078};
1079
Michal Vaskof0c92c02016-01-29 09:41:45 +01001080static struct CRYPTO_dynlock_value *
1081tls_dyn_create_func(const char *UNUSED(file), int UNUSED(line))
1082{
1083 struct CRYPTO_dynlock_value *value;
1084
1085 value = malloc(sizeof *value);
1086 if (!value) {
1087 ERRMEM;
1088 return NULL;
1089 }
1090 pthread_mutex_init(&value->lock, NULL);
1091
1092 return value;
1093}
1094
1095static void
1096tls_dyn_lock_func(int mode, struct CRYPTO_dynlock_value *l, const char *UNUSED(file), int UNUSED(line))
1097{
1098 /* mode can also be CRYPTO_READ or CRYPTO_WRITE, but all the examples
1099 * I found ignored this fact, what do I know... */
1100 if (mode & CRYPTO_LOCK) {
1101 pthread_mutex_lock(&l->lock);
1102 } else {
1103 pthread_mutex_unlock(&l->lock);
1104 }
1105}
1106
1107static void
1108tls_dyn_destroy_func(struct CRYPTO_dynlock_value *l, const char *UNUSED(file), int UNUSED(line))
1109{
1110 pthread_mutex_destroy(&l->lock);
1111 free(l);
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001112}
1113
Michal Vasko8f0c0282016-02-29 10:17:14 +01001114#endif /* NC_ENABLED_TLS */
1115
1116#if defined(NC_ENABLED_TLS) && !defined(NC_ENABLED_SSH)
1117
1118static pthread_mutex_t *tls_locks;
1119
1120static void
1121tls_thread_locking_func(int mode, int n, const char *UNUSED(file), int UNUSED(line))
1122{
1123 if (mode & CRYPTO_LOCK) {
1124 pthread_mutex_lock(tls_locks + n);
1125 } else {
1126 pthread_mutex_unlock(tls_locks + n);
1127 }
1128}
1129
1130static void
1131tls_thread_id_func(CRYPTO_THREADID *tid)
1132{
1133 CRYPTO_THREADID_set_numeric(tid, (unsigned long)pthread_self());
1134}
1135
1136static void
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001137nc_tls_init(void)
1138{
1139 int i;
1140
1141 SSL_load_error_strings();
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001142 ERR_load_BIO_strings();
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001143 SSL_library_init();
1144
1145 tls_locks = malloc(CRYPTO_num_locks() * sizeof *tls_locks);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001146 if (!tls_locks) {
1147 ERRMEM;
1148 return;
1149 }
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001150 for (i = 0; i < CRYPTO_num_locks(); ++i) {
1151 pthread_mutex_init(tls_locks + i, NULL);
1152 }
1153
Michal Vaskof0c92c02016-01-29 09:41:45 +01001154 CRYPTO_THREADID_set_callback(tls_thread_id_func);
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001155 CRYPTO_set_locking_callback(tls_thread_locking_func);
Michal Vaskof0c92c02016-01-29 09:41:45 +01001156
1157 CRYPTO_set_dynlock_create_callback(tls_dyn_create_func);
1158 CRYPTO_set_dynlock_lock_callback(tls_dyn_lock_func);
1159 CRYPTO_set_dynlock_destroy_callback(tls_dyn_destroy_func);
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001160}
1161
Michal Vasko8f0c0282016-02-29 10:17:14 +01001162static void
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001163nc_tls_destroy(void)
1164{
1165 int i;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001166
Michal Vasko8f0c0282016-02-29 10:17:14 +01001167 FIPS_mode_set(0);
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001168 CRYPTO_cleanup_all_ex_data();
Michal Vaskob6e37262016-02-25 14:49:00 +01001169 nc_thread_destroy();
Michal Vasko5e228792016-02-03 15:30:24 +01001170 EVP_cleanup();
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001171 ERR_free_strings();
Jan Kundrát47e9e1a2016-11-21 09:41:59 +01001172#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0
1173 // no de-init needed
1174#elif OPENSSL_VERSION_NUMBER >= 0x10002000L // >= 1.0.2
1175 SSL_COMP_free_compression_methods();
1176#else
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001177 sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
Jan Kundrát47e9e1a2016-11-21 09:41:59 +01001178#endif
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001179
Michal Vaskob6e37262016-02-25 14:49:00 +01001180 CRYPTO_THREADID_set_callback(NULL);
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001181 CRYPTO_set_locking_callback(NULL);
1182 for (i = 0; i < CRYPTO_num_locks(); ++i) {
1183 pthread_mutex_destroy(tls_locks + i);
1184 }
1185 free(tls_locks);
Michal Vaskof0c92c02016-01-29 09:41:45 +01001186
1187 CRYPTO_set_dynlock_create_callback(NULL);
1188 CRYPTO_set_dynlock_lock_callback(NULL);
1189 CRYPTO_set_dynlock_destroy_callback(NULL);
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001190}
1191
Michal Vasko8f0c0282016-02-29 10:17:14 +01001192#endif /* NC_ENABLED_TLS && !NC_ENABLED_SSH */
Michal Vasko5e228792016-02-03 15:30:24 +01001193
Radek Krejci53691be2016-02-22 13:58:37 +01001194#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
Michal Vasko5e228792016-02-03 15:30:24 +01001195
Michal Vasko8f0c0282016-02-29 10:17:14 +01001196static void
Michal Vasko5e228792016-02-03 15:30:24 +01001197nc_ssh_tls_init(void)
1198{
1199 SSL_load_error_strings();
1200 ERR_load_BIO_strings();
1201 SSL_library_init();
1202
1203 nc_ssh_init();
1204
1205 CRYPTO_set_dynlock_create_callback(tls_dyn_create_func);
1206 CRYPTO_set_dynlock_lock_callback(tls_dyn_lock_func);
1207 CRYPTO_set_dynlock_destroy_callback(tls_dyn_destroy_func);
1208}
1209
Michal Vasko8f0c0282016-02-29 10:17:14 +01001210static void
Michal Vasko5e228792016-02-03 15:30:24 +01001211nc_ssh_tls_destroy(void)
1212{
1213 ERR_free_strings();
Jan Kundrát47e9e1a2016-11-21 09:41:59 +01001214#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0
1215 // no de-init needed
1216#elif OPENSSL_VERSION_NUMBER >= 0x10002000L // >= 1.0.2
1217 SSL_COMP_free_compression_methods();
1218#else
Michal Vasko5e228792016-02-03 15:30:24 +01001219 sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
Jan Kundrát47e9e1a2016-11-21 09:41:59 +01001220#endif
Michal Vasko5e228792016-02-03 15:30:24 +01001221
1222 nc_ssh_destroy();
1223
1224 CRYPTO_set_dynlock_create_callback(NULL);
1225 CRYPTO_set_dynlock_lock_callback(NULL);
1226 CRYPTO_set_dynlock_destroy_callback(NULL);
1227}
1228
Radek Krejci53691be2016-02-22 13:58:37 +01001229#endif /* NC_ENABLED_SSH && NC_ENABLED_TLS */
Michal Vasko8f0c0282016-02-29 10:17:14 +01001230
1231#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
1232
1233API void
1234nc_thread_destroy(void)
1235{
1236 CRYPTO_THREADID crypto_tid;
1237
1238 /* caused data-races and seems not neccessary for avoiding valgrind reachable memory */
1239 //CRYPTO_cleanup_all_ex_data();
1240
1241 CRYPTO_THREADID_current(&crypto_tid);
1242 ERR_remove_thread_state(&crypto_tid);
1243}
1244
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001245#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
1246
1247void
Michal Vasko8f0c0282016-02-29 10:17:14 +01001248nc_init(void)
1249{
1250#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
1251 nc_ssh_tls_init();
1252#elif defined(NC_ENABLED_SSH)
1253 nc_ssh_init();
1254#elif defined(NC_ENABLED_TLS)
1255 nc_tls_init();
1256#endif
1257}
1258
Michal Vaskoa7b8ca52016-03-01 12:09:29 +01001259void
Michal Vasko8f0c0282016-02-29 10:17:14 +01001260nc_destroy(void)
1261{
1262#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
1263 nc_ssh_tls_destroy();
1264#elif defined(NC_ENABLED_SSH)
1265 nc_ssh_destroy();
1266#elif defined(NC_ENABLED_TLS)
1267 nc_tls_destroy();
1268#endif
1269}