blob: b3ae61a8c058e0b7c520db7a807c6271916f6c2f [file] [log] [blame]
Radek Krejci9f03b482015-10-22 16:02:10 +02001/**
Michal Vasko086311b2016-01-08 09:53:11 +01002 * \file session_client_tls.c
Radek Krejci9f03b482015-10-22 16:02:10 +02003 * \author Radek Krejci <rkrejci@cesnet.cz>
Michal Vasko11d4cdb2015-10-29 11:42:52 +01004 * \author Michal Vasko <mvasko@cesnet.cz>
Michal Vasko086311b2016-01-08 09:53:11 +01005 * \brief libnetconf2 - TLS specific session client transport functions
Radek Krejci9f03b482015-10-22 16:02:10 +02006 *
Michal Vasko11d4cdb2015-10-29 11:42:52 +01007 * This source is compiled only with libssl.
Radek Krejci9f03b482015-10-22 16:02:10 +02008 *
9 * Copyright (c) 2015 CESNET, z.s.p.o.
10 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010011 * This source code is licensed under BSD 3-Clause License (the "License").
12 * You may not use this file except in compliance with the License.
13 * You may obtain a copy of the License at
14 *
15 * https://opensource.org/licenses/BSD-3-Clause
Radek Krejci9f03b482015-10-22 16:02:10 +020016 */
17
18#include <assert.h>
19#include <errno.h>
Radek Krejci9f03b482015-10-22 16:02:10 +020020#include <string.h>
21#include <unistd.h>
22
23#include <libyang/libyang.h>
Michal Vasko086311b2016-01-08 09:53:11 +010024#include <openssl/err.h>
Michal Vaskodae48322016-02-25 14:49:48 +010025#include <openssl/x509.h>
Radek Krejci9f03b482015-10-22 16:02:10 +020026
Michal Vaskoe22c6732016-01-29 11:03:02 +010027#include "session_client.h"
28#include "session_client_ch.h"
Radek Krejci9f03b482015-10-22 16:02:10 +020029#include "libnetconf.h"
Michal Vasko11d4cdb2015-10-29 11:42:52 +010030
Michal Vaskodaf9a092016-02-09 10:42:05 +010031extern struct nc_client_opts client_opts;
Michal Vasko3031aae2016-01-27 16:07:18 +010032static struct nc_client_tls_opts tls_opts;
33static struct nc_client_tls_opts tls_ch_opts;
34
35static int tlsauth_ch;
Radek Krejci9f03b482015-10-22 16:02:10 +020036
Radek Krejci9f03b482015-10-22 16:02:10 +020037static int
Michal Vasko11d4cdb2015-10-29 11:42:52 +010038tlsauth_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
Radek Krejci9f03b482015-10-22 16:02:10 +020039{
Michal Vasko11d4cdb2015-10-29 11:42:52 +010040 X509_STORE_CTX store_ctx;
41 X509_OBJECT obj;
42 X509_NAME *subject, *issuer;
43 X509 *cert;
44 X509_CRL *crl;
45 X509_REVOKED *revoked;
46 EVP_PKEY *pubkey;
47 int i, n, rc;
48 ASN1_TIME *next_update = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +010049 struct nc_client_tls_opts *opts;
Radek Krejci9f03b482015-10-22 16:02:10 +020050
Michal Vasko11d4cdb2015-10-29 11:42:52 +010051 if (!preverify_ok) {
52 return 0;
53 }
Radek Krejci9f03b482015-10-22 16:02:10 +020054
Michal Vasko3031aae2016-01-27 16:07:18 +010055 opts = (tlsauth_ch ? &tls_ch_opts : &tls_opts);
56
57 if (!opts->crl_store) {
58 /* nothing to check */
59 return 1;
60 }
61
Michal Vasko11d4cdb2015-10-29 11:42:52 +010062 cert = X509_STORE_CTX_get_current_cert(x509_ctx);
63 subject = X509_get_subject_name(cert);
64 issuer = X509_get_issuer_name(cert);
65
66 /* try to retrieve a CRL corresponding to the _subject_ of
67 * the current certificate in order to verify it's integrity */
68 memset((char *)&obj, 0, sizeof obj);
Michal Vasko3031aae2016-01-27 16:07:18 +010069 X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
Michal Vasko11d4cdb2015-10-29 11:42:52 +010070 rc = X509_STORE_get_by_subject(&store_ctx, X509_LU_CRL, subject, &obj);
71 X509_STORE_CTX_cleanup(&store_ctx);
72 crl = obj.data.crl;
73 if (rc > 0 && crl) {
74 next_update = X509_CRL_get_nextUpdate(crl);
75
76 /* verify the signature on this CRL */
77 pubkey = X509_get_pubkey(cert);
78 if (X509_CRL_verify(crl, pubkey) <= 0) {
79 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
80 X509_OBJECT_free_contents(&obj);
81 if (pubkey) {
82 EVP_PKEY_free(pubkey);
83 }
84 return 0; /* fail */
85 }
86 if (pubkey) {
87 EVP_PKEY_free(pubkey);
88 }
89
90 /* check date of CRL to make sure it's not expired */
91 if (!next_update) {
92 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
93 X509_OBJECT_free_contents(&obj);
94 return 0; /* fail */
95 }
96 if (X509_cmp_current_time(next_update) < 0) {
97 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED);
98 X509_OBJECT_free_contents(&obj);
99 return 0; /* fail */
100 }
101 X509_OBJECT_free_contents(&obj);
102 }
103
104 /* try to retrieve a CRL corresponding to the _issuer_ of
105 * the current certificate in order to check for revocation */
106 memset((char *)&obj, 0, sizeof obj);
Michal Vasko3031aae2016-01-27 16:07:18 +0100107 X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100108 rc = X509_STORE_get_by_subject(&store_ctx, X509_LU_CRL, issuer, &obj);
109 X509_STORE_CTX_cleanup(&store_ctx);
110 crl = obj.data.crl;
111 if (rc > 0 && crl) {
112 /* check if the current certificate is revoked by this CRL */
113 n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
114 for (i = 0; i < n; i++) {
115 revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
116 if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(cert)) == 0) {
117 ERR("Certificate revoked!");
118 X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED);
119 X509_OBJECT_free_contents(&obj);
120 return 0; /* fail */
121 }
122 }
123 X509_OBJECT_free_contents(&obj);
124 }
125
126 return 1; /* success */
127}
128
Michal Vaskoe22c6732016-01-29 11:03:02 +0100129static void
130_nc_client_tls_destroy_opts(struct nc_client_tls_opts *opts)
131{
132 free(opts->cert_path);
133 free(opts->key_path);
134 free(opts->ca_file);
135 free(opts->ca_dir);
136 SSL_CTX_free(opts->tls_ctx);
137
138 free(opts->crl_file);
139 free(opts->crl_dir);
140 X509_STORE_free(opts->crl_store);
141}
142
143API void
144nc_client_tls_destroy_opts(void)
145{
146 _nc_client_tls_destroy_opts(&tls_opts);
147 _nc_client_tls_destroy_opts(&tls_ch_opts);
148}
149
Michal Vasko3031aae2016-01-27 16:07:18 +0100150static int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100151_nc_client_tls_set_cert_key_paths(const char *client_cert, const char *client_key, struct nc_client_tls_opts *opts)
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100152{
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100153 if (!client_cert) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100154 ERRARG;
155 return -1;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100156 }
157
Michal Vasko3031aae2016-01-27 16:07:18 +0100158 free(opts->cert_path);
159 free(opts->key_path);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100160
Michal Vasko9db2a6f2016-02-01 13:26:03 +0100161 opts->cert_path = strdup(client_cert);
162 if (!opts->cert_path) {
163 ERRMEM;
164 return -1;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100165 }
166
Michal Vasko3031aae2016-01-27 16:07:18 +0100167 if (client_key) {
168 opts->key_path = strdup(client_key);
Michal Vasko9db2a6f2016-02-01 13:26:03 +0100169 if (!opts->key_path) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100170 ERRMEM;
171 return -1;
172 }
173 } else {
174 opts->key_path = NULL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100175 }
176
Michal Vasko3031aae2016-01-27 16:07:18 +0100177 opts->tls_ctx_change = 1;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100178
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100179 return 0;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100180}
181
Michal Vasko3031aae2016-01-27 16:07:18 +0100182API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100183nc_client_tls_set_cert_key_paths(const char *client_cert, const char *client_key)
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100184{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100185 return _nc_client_tls_set_cert_key_paths(client_cert, client_key, &tls_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100186}
187
188API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100189nc_client_tls_ch_set_cert_key_paths(const char *client_cert, const char *client_key)
Michal Vasko3031aae2016-01-27 16:07:18 +0100190{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100191 return _nc_client_tls_set_cert_key_paths(client_cert, client_key, &tls_ch_opts);
192}
193
194static void
195_nc_client_tls_get_cert_key_paths(const char **client_cert, const char **client_key, struct nc_client_tls_opts *opts)
196{
197 if (!client_cert && !client_key) {
198 ERRARG;
199 return;
200 }
201
202 if (client_cert) {
203 *client_cert = opts->cert_path;
204 }
205 if (client_key) {
206 *client_key = opts->key_path;
207 }
208}
209
210API void
211nc_client_tls_get_cert_key_paths(const char **client_cert, const char **client_key)
212{
213 _nc_client_tls_get_cert_key_paths(client_cert, client_key, &tls_opts);
214}
215
216API void
217nc_client_tls_ch_get_cert_key_paths(const char **client_cert, const char **client_key)
218{
219 _nc_client_tls_get_cert_key_paths(client_cert, client_key, &tls_ch_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100220}
221
222static int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100223_nc_client_tls_set_trusted_ca_paths(const char *ca_file, const char *ca_dir, struct nc_client_tls_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100224{
Michal Vasko3031aae2016-01-27 16:07:18 +0100225 if (!ca_file && !ca_dir) {
226 ERRARG;
227 return -1;
228 }
229
Michal Vasko3031aae2016-01-27 16:07:18 +0100230 free(opts->ca_file);
231 free(opts->ca_dir);
232
233 if (ca_file) {
234 opts->ca_file = strdup(ca_file);
235 if (!opts->ca_file) {
236 ERRMEM;
237 return -1;
238 }
239 } else {
240 opts->ca_file = NULL;
241 }
242
243 if (ca_dir) {
244 opts->ca_dir = strdup(ca_dir);
245 if (!opts->ca_dir) {
246 ERRMEM;
247 return -1;
248 }
249 } else {
250 opts->ca_dir = NULL;
251 }
252
253 opts->tls_ctx_change = 1;
254
255 return 0;
256}
257
258API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100259nc_client_tls_set_trusted_ca_paths(const char *ca_file, const char *ca_dir)
Michal Vasko3031aae2016-01-27 16:07:18 +0100260{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100261 return _nc_client_tls_set_trusted_ca_paths(ca_file, ca_dir, &tls_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100262}
263
264API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100265nc_client_tls_ch_set_trusted_ca_paths(const char *ca_file, const char *ca_dir)
Michal Vasko3031aae2016-01-27 16:07:18 +0100266{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100267 return _nc_client_tls_set_trusted_ca_paths(ca_file, ca_dir, &tls_ch_opts);
268}
269
270static void
271_nc_client_tls_get_trusted_ca_paths(const char **ca_file, const char **ca_dir, struct nc_client_tls_opts *opts)
272{
273 if (!ca_file && !ca_dir) {
274 ERRARG;
275 return;
276 }
277
278 if (ca_file) {
279 *ca_file = opts->ca_file;
280 }
281 if (ca_dir) {
282 *ca_dir = opts->ca_dir;
283 }
284}
285
286API void
287nc_client_tls_get_trusted_ca_paths(const char **ca_file, const char **ca_dir)
288{
289 _nc_client_tls_get_trusted_ca_paths(ca_file, ca_dir, &tls_opts);
290}
291
292API void
293nc_client_tls_ch_get_trusted_ca_paths(const char **ca_file, const char **ca_dir)
294{
295 _nc_client_tls_get_trusted_ca_paths(ca_file, ca_dir, &tls_ch_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100296}
297
298static int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100299_nc_client_tls_set_crl_paths(const char *crl_file, const char *crl_dir, struct nc_client_tls_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100300{
Michal Vasko3031aae2016-01-27 16:07:18 +0100301 if (!crl_file && !crl_dir) {
302 ERRARG;
303 return -1;
304 }
305
Michal Vasko3031aae2016-01-27 16:07:18 +0100306 free(opts->crl_file);
307 free(opts->crl_dir);
308
309 if (crl_file) {
310 opts->crl_file = strdup(crl_file);
311 if (!opts->crl_file) {
312 ERRMEM;
313 return -1;
314 }
315 } else {
316 opts->crl_file = NULL;
317 }
318
319 if (crl_dir) {
320 opts->crl_dir = strdup(crl_dir);
321 if (!opts->crl_dir) {
322 ERRMEM;
323 return -1;
324 }
325 } else {
326 opts->crl_dir = NULL;
327 }
328
329 opts->crl_store_change = 1;
330
331 return 0;
332}
333
334API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100335nc_client_tls_set_crl_paths(const char *crl_file, const char *crl_dir)
Michal Vasko3031aae2016-01-27 16:07:18 +0100336{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100337 return _nc_client_tls_set_crl_paths(crl_file, crl_dir, &tls_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100338}
339
340API int
Michal Vaskoe22c6732016-01-29 11:03:02 +0100341nc_client_tls_ch_set_crl_paths(const char *crl_file, const char *crl_dir)
Michal Vasko3031aae2016-01-27 16:07:18 +0100342{
Michal Vaskoe22c6732016-01-29 11:03:02 +0100343 return _nc_client_tls_set_crl_paths(crl_file, crl_dir, &tls_ch_opts);
344}
345
346static void
347_nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir, struct nc_client_tls_opts *opts)
348{
349 if (!crl_file && !crl_dir) {
350 ERRARG;
351 return;
352 }
353
354 if (crl_file) {
355 *crl_file = opts->crl_file;
356 }
357 if (crl_dir) {
358 *crl_dir = opts->crl_dir;
359 }
360}
361
362API void
363nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir)
364{
365 _nc_client_tls_get_crl_paths(crl_file, crl_dir, &tls_opts);
366}
367
368API void
369nc_client_tls_ch_get_crl_paths(const char **crl_file, const char **crl_dir)
370{
371 _nc_client_tls_get_crl_paths(crl_file, crl_dir, &tls_ch_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100372}
373
374API int
375nc_client_tls_ch_add_bind_listen(const char *address, uint16_t port)
376{
377 return nc_client_ch_add_bind_listen(address, port, NC_TI_OPENSSL);
378}
379
380API int
381nc_client_tls_ch_del_bind(const char *address, uint16_t port)
382{
383 return nc_client_ch_del_bind(address, port, NC_TI_OPENSSL);
384}
385
Michal Vasko3031aae2016-01-27 16:07:18 +0100386static int
Michal Vaskoaf58cd92016-01-28 11:56:02 +0100387nc_client_tls_update_opts(struct nc_client_tls_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100388{
389 char *key;
390 X509_LOOKUP *lookup;
Michal Vasko3031aae2016-01-27 16:07:18 +0100391
392 if (!opts->tls_ctx || opts->tls_ctx_change) {
Michal Vaskoe22c6732016-01-29 11:03:02 +0100393 SSL_CTX_free(opts->tls_ctx);
Michal Vasko3031aae2016-01-27 16:07:18 +0100394
395 /* prepare global SSL context, allow only mandatory TLS 1.2 */
396 if (!(opts->tls_ctx = SSL_CTX_new(TLSv1_2_client_method()))) {
397 ERR("Unable to create OpenSSL context (%s).", ERR_reason_error_string(ERR_get_error()));
398 return -1;
399 }
400 SSL_CTX_set_verify(opts->tls_ctx, SSL_VERIFY_PEER, tlsauth_verify_callback);
401
402 /* get peer certificate */
403 if (SSL_CTX_use_certificate_file(opts->tls_ctx, opts->cert_path, SSL_FILETYPE_PEM) != 1) {
404 ERR("Loading the client certificate from \'%s\' failed (%s).", opts->cert_path, ERR_reason_error_string(ERR_get_error()));
405 return -1;
406 }
407
408 /* if the file with private key not specified, expect that the private key is stored with the certificate */
409 if (!opts->key_path) {
410 key = opts->cert_path;
411 } else {
412 key = opts->key_path;
413 }
414 if (SSL_CTX_use_PrivateKey_file(opts->tls_ctx, key, SSL_FILETYPE_PEM) != 1) {
415 ERR("Loading the client priavte key from \'%s\' failed (%s).", key, ERR_reason_error_string(ERR_get_error()));
416 return -1;
417 }
418
419 if (!SSL_CTX_load_verify_locations(opts->tls_ctx, opts->ca_file, opts->ca_dir)) {
420 ERR("Failed to load the locations of trusted CA certificates (%s).", ERR_reason_error_string(ERR_get_error()));
421 return -1;
422 }
423 }
424
425 if (opts->crl_store_change || (!opts->crl_store && (opts->crl_file || opts->crl_dir))) {
426 /* set the revocation store with the correct paths for the callback */
427 X509_STORE_free(opts->crl_store);
428
429 opts->crl_store = X509_STORE_new();
430 if (!opts->crl_store) {
431 ERR("Unable to create a certificate store (%s).", ERR_reason_error_string(ERR_get_error()));
432 return -1;
433 }
434 opts->crl_store->cache = 0;
435
436 if (opts->crl_file) {
437 if (!(lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_file()))) {
438 ERR("Failed to add lookup method to CRL checking.");
439 return -1;
440 }
441 if (X509_LOOKUP_add_dir(lookup, opts->crl_file, X509_FILETYPE_PEM) != 1) {
442 ERR("Failed to add the revocation lookup file \"%s\".", opts->crl_file);
443 return -1;
444 }
445 }
446
447 if (opts->crl_dir) {
448 if (!(lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_hash_dir()))) {
449 ERR("Failed to add lookup method to CRL checking.");
450 return -1;
451 }
452 if (X509_LOOKUP_add_dir(lookup, opts->crl_dir, X509_FILETYPE_PEM) != 1) {
453 ERR("Failed to add the revocation lookup directory \"%s\".", opts->crl_dir);
454 return -1;
455 }
456 }
457 }
458
459 return 0;
Radek Krejci9f03b482015-10-22 16:02:10 +0200460}
461
462API struct nc_session *
Michal Vasko9bee18d2015-12-08 14:41:42 +0100463nc_connect_tls(const char *host, unsigned short port, struct ly_ctx *ctx)
Radek Krejci9f03b482015-10-22 16:02:10 +0200464{
Radek Krejci9f03b482015-10-22 16:02:10 +0200465 struct nc_session *session = NULL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100466 int sock, verify;
467
Michal Vasko3031aae2016-01-27 16:07:18 +0100468 if (!tls_opts.cert_path || (!tls_opts.ca_file && !tls_opts.ca_dir)) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100469 ERRARG;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100470 return NULL;
471 }
Radek Krejci9f03b482015-10-22 16:02:10 +0200472
473 /* process parameters */
474 if (!host || strisempty(host)) {
475 host = "localhost";
476 }
477
478 if (!port) {
479 port = NC_PORT_TLS;
480 }
481
Michal Vasko3031aae2016-01-27 16:07:18 +0100482 /* create/update TLS structures */
Michal Vaskoaf58cd92016-01-28 11:56:02 +0100483 if (nc_client_tls_update_opts(&tls_opts)) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100484 return NULL;
485 }
486
Radek Krejci9f03b482015-10-22 16:02:10 +0200487 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100488 session = calloc(1, sizeof *session);
Radek Krejci9f03b482015-10-22 16:02:10 +0200489 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100490 ERRMEM;
Radek Krejci9f03b482015-10-22 16:02:10 +0200491 return NULL;
492 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100493 session->status = NC_STATUS_STARTING;
494 session->side = NC_CLIENT;
Radek Krejci9f03b482015-10-22 16:02:10 +0200495
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100496 /* transport lock */
497 session->ti_lock = malloc(sizeof *session->ti_lock);
498 if (!session->ti_lock) {
499 ERRMEM;
500 goto fail;
501 }
502 pthread_mutex_init(session->ti_lock, NULL);
503
504 /* fill the session */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100505 session->ti_type = NC_TI_OPENSSL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100506 if (!(session->ti.tls = SSL_new(tls_opts.tls_ctx))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100507 ERR("Failed to create a new TLS session structure (%s).", ERR_reason_error_string(ERR_get_error()));
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100508 goto fail;
509 }
Radek Krejci9f03b482015-10-22 16:02:10 +0200510
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100511 /* create and assign socket */
Michal Vaskof05562c2016-01-20 12:06:43 +0100512 sock = nc_sock_connect(host, port);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100513 if (sock == -1) {
514 goto fail;
515 }
516 SSL_set_fd(session->ti.tls, sock);
517
518 /* set the SSL_MODE_AUTO_RETRY flag to allow OpenSSL perform re-handshake automatically */
519 SSL_set_mode(session->ti.tls, SSL_MODE_AUTO_RETRY);
520
521 /* connect and perform the handshake */
Michal Vasko3031aae2016-01-27 16:07:18 +0100522 tlsauth_ch = 0;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100523 if (SSL_connect(session->ti.tls) != 1) {
524 ERR("Connecting over TLS failed (%s).", ERR_reason_error_string(ERR_get_error()));
525 goto fail;
526 }
527
528 /* check certificate verification result */
529 verify = SSL_get_verify_result(session->ti.tls);
530 switch (verify) {
531 case X509_V_OK:
532 VRB("Server certificate successfully verified.");
533 break;
534 default:
Michal Vaskob48aa812016-01-18 14:13:09 +0100535 WRN("Server certificate verification problem (%s).", X509_verify_cert_error_string(verify));
Radek Krejci9f03b482015-10-22 16:02:10 +0200536 }
537
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100538 /* assign context (dicionary needed for handshake) */
539 if (!ctx) {
Michal Vaskodaf9a092016-02-09 10:42:05 +0100540 if (client_opts.schema_searchpath) {
541 ctx = ly_ctx_new(client_opts.schema_searchpath);
542 } else {
543 ctx = ly_ctx_new(SCHEMAS_DIR);
544 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100545 } else {
546 session->flags |= NC_SESSION_SHAREDCTX;
547 }
548 session->ctx = ctx;
549
Radek Krejci9f03b482015-10-22 16:02:10 +0200550 /* NETCONF handshake */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100551 if (nc_handshake(session)) {
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100552 goto fail;
Radek Krejci9f03b482015-10-22 16:02:10 +0200553 }
Michal Vaskoad611702015-12-03 13:41:51 +0100554 session->status = NC_STATUS_RUNNING;
Radek Krejci9f03b482015-10-22 16:02:10 +0200555
Michal Vaskoef578332016-01-25 13:20:09 +0100556 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +0100557 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100558 }
559
560 /* store information into session and the dictionary */
561 session->host = lydict_insert(ctx, host, 0);
562 session->port = port;
Michal Vasko9bee18d2015-12-08 14:41:42 +0100563 session->username = lydict_insert(ctx, "certificate-based", 0);
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100564
Radek Krejci9f03b482015-10-22 16:02:10 +0200565 return session;
566
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100567fail:
Radek Krejci9f03b482015-10-22 16:02:10 +0200568 nc_session_free(session);
569 return NULL;
570}
571
572API struct nc_session *
573nc_connect_libssl(SSL *tls, struct ly_ctx *ctx)
574{
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100575 struct nc_session *session;
Radek Krejci9f03b482015-10-22 16:02:10 +0200576
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100577 /* check TLS session status */
578 if (!tls || !SSL_is_init_finished(tls)) {
579 ERR("Supplied TLS session is not fully connected!");
580 return NULL;
581 }
582
583 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100584 session = calloc(1, sizeof *session);
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100585 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100586 ERRMEM;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100587 return NULL;
588 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100589 session->status = NC_STATUS_STARTING;
590 session->side = NC_CLIENT;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100591
592 /* transport lock */
593 session->ti_lock = malloc(sizeof *session->ti_lock);
594 if (!session->ti_lock) {
595 ERRMEM;
596 goto fail;
597 }
598 pthread_mutex_init(session->ti_lock, NULL);
599
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100600 session->ti_type = NC_TI_OPENSSL;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100601 session->ti.tls = tls;
602
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100603 /* assign context (dicionary needed for handshake) */
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100604 if (!ctx) {
Michal Vaskodaf9a092016-02-09 10:42:05 +0100605 if (client_opts.schema_searchpath) {
606 ctx = ly_ctx_new(client_opts.schema_searchpath);
607 } else {
608 ctx = ly_ctx_new(SCHEMAS_DIR);
609 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100610 } else {
611 session->flags |= NC_SESSION_SHAREDCTX;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100612 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100613 session->ctx = ctx;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100614
615 /* NETCONF handshake */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100616 if (nc_handshake(session)) {
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100617 goto fail;
618 }
Michal Vaskoad611702015-12-03 13:41:51 +0100619 session->status = NC_STATUS_RUNNING;
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100620
Michal Vaskoef578332016-01-25 13:20:09 +0100621 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +0100622 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100623 }
624
Michal Vasko11d4cdb2015-10-29 11:42:52 +0100625 return session;
626
627fail:
628 nc_session_free(session);
Radek Krejci9f03b482015-10-22 16:02:10 +0200629 return NULL;
630}
631
Michal Vasko3031aae2016-01-27 16:07:18 +0100632struct nc_session *
Michal Vaskoaf58cd92016-01-28 11:56:02 +0100633nc_accept_callhome_tls_sock(int sock, const char *host, uint16_t port, struct ly_ctx *ctx)
Michal Vasko80cad7f2015-12-08 14:42:27 +0100634{
Michal Vasko3031aae2016-01-27 16:07:18 +0100635 int verify;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100636 SSL *tls;
637 struct nc_session *session;
638
Michal Vaskoaf58cd92016-01-28 11:56:02 +0100639 if (nc_client_tls_update_opts(&tls_ch_opts)) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100640 close(sock);
Michal Vaskoc61c4492016-01-25 11:13:34 +0100641 return NULL;
642 }
643
Michal Vasko3031aae2016-01-27 16:07:18 +0100644 if (!(tls = SSL_new(tls_ch_opts.tls_ctx))) {
Michal Vasko80cad7f2015-12-08 14:42:27 +0100645 ERR("Failed to create new TLS session structure (%s).", ERR_reason_error_string(ERR_get_error()));
646 close(sock);
647 return NULL;
648 }
649
650 SSL_set_fd(tls, sock);
651
652 /* set the SSL_MODE_AUTO_RETRY flag to allow OpenSSL perform re-handshake automatically */
653 SSL_set_mode(tls, SSL_MODE_AUTO_RETRY);
654
655 /* connect and perform the handshake */
Michal Vasko3031aae2016-01-27 16:07:18 +0100656 tlsauth_ch = 1;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100657 if (SSL_connect(tls) != 1) {
658 ERR("Connecting over TLS failed (%s).", ERR_reason_error_string(ERR_get_error()));
659 SSL_free(tls);
660 return NULL;
661 }
662
663 /* check certificate verification result */
664 verify = SSL_get_verify_result(tls);
665 switch (verify) {
666 case X509_V_OK:
667 VRB("Server certificate successfully verified.");
668 break;
669 default:
Michal Vaskob48aa812016-01-18 14:13:09 +0100670 WRN("Server certificate verification problem (%s).", X509_verify_cert_error_string(verify));
Michal Vasko80cad7f2015-12-08 14:42:27 +0100671 }
672
673 session = nc_connect_libssl(tls, ctx);
674 if (session) {
Michal Vasko4282fae2016-02-18 10:03:42 +0100675 session->flags |= NC_SESSION_CALLHOME;
676
Michal Vasko80cad7f2015-12-08 14:42:27 +0100677 /* store information into session and the dictionary */
Michal Vasko3031aae2016-01-27 16:07:18 +0100678 session->host = lydict_insert(session->ctx, host, 0);
Michal Vasko80cad7f2015-12-08 14:42:27 +0100679 session->port = port;
680 session->username = lydict_insert(session->ctx, "certificate-based", 0);
681 }
682
683 return session;
684}